beginning microsofl sql server 2008 programming phần 6 pot

73 289 0
beginning microsofl sql server 2008 programming phần 6 pot

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

Setting Variables Using SET SET is usually used for setting variables in the fashion that you would see in more procedural languages. Examples of typical uses would be: SET @TotalCost = 10 SET @TotalCost = @UnitCost * 1.1 Notice that these are all straight assignments that use either explicit values or another variable. With a SET, you cannot assign a value to a variable from a query — you have to separate the query from the SET. For example: USE AdventureWorks2008; DECLARE @Test money; SET @Test = MAX(UnitPrice) FROM [Order Details]; SELECT @Test; causes an error, but: USE AdventureWorks2008; DECLARE @Test money; SET @Test = (SELECT MAX(UnitPrice) FROM Sales.SalesOrderDetail); SELECT @Test; works just fine. Although this latter syntax works, by convention, code is never implemented this way. Again, I don’t know for sure why it’s “just not done that way,” but I suspect that it has to do with readability — you want a SELECT statement to be related to retrieving table data, and a SET to be about simple variable assignments. Setting Variables Using SELECT SELECT is usually used to assign variable values when the source of the information you’re storing in the variable is from a query. For example, our last illustration would be typically done using a SELECT: USE AdventureWorks2008; DECLARE @Test money; SELECT @Test = MAX(UnitPrice) FROM Sales.SalesOrderDetail; SELECT @Test; Notice that this is a little cleaner (it takes less verbiage to do the same thing). So again, the convention on when to use which goes like this: ❑ Use SET when you are performing a simple assignment of a variable — where your value is already known in the form of an explicit value or some other variable. ❑ Use SELECT when you are basing the assignment of your variable on a query. 328 Chapter 11: Writing Scripts and Batches 57012c11.qxd:WroxBeg 11/25/08 5:53 AM Page 328 I’m not going to pick any bones about the fact that you’ll see me violate this last convention in many places in this book. Using SET for variable assignment first appeared in version 7.0, and I must admit that nearly a decade after that release, I still haven’t completely adapted yet. Nonetheless, this seems to be something that’s really being pushed by Microsoft and the SQL Server community, so I strongly rec- ommend that you start out on the right foot and adhere to the convention. Reviewing System Functions There are over 30 parameterless system functions available. The older ones in the mix start with an @@ sign — a throwback to when they were commonly referred to as “Global Variables.” Thankfully that name has gone away in favor of the more accurate “system functions,” and the majority of all system functions now come without the @@ prefix. Some of the ones you should be most concerned with are in the table that follows: Continued Variable Purpose Comments @@DATEFIRST Returns what is cur- rently set as the first day of the week (say, Sunday vs. Monday). Is a system-wide setting — if someone changes the setting, you may not get the result you expect. @@ERROR Returns the error number of the last T- SQL statement exe- cuted on the current connection. Returns 0 if no error. Is reset with each new statement. If you need the value preserved, move it to a local variable immediately after the execution of the statement for which you want to preserve the error code. @@IDENTITY Returns the last identity value inserted as a result of the last INSERT or SELECT INTO state- ment. Is set to NULL if no identity value was generated. This is true even if the lack of an identity value was due to a failure of the statement to run. If multiple inserts are performed by just one state- ment, then only the last identity value is returned. IDENT_CURRENT() Returns the last identity value inserted for a speci- fied table regardless of session or scope. Nice in the sense that it doesn’t get overwritten if you’re inserting into multiple tables, but can give you a value other than what you were expecting if other connections are inserting into the specific table. @@OPTIONS Returns information about options that have been set using the SET command. Since you get only one value back, but can have many options set, SQL Server uses binary flags to indicate what values are set. To test whether the option you are interested is set, you must use the option value together with a bitwise operator. 329 Chapter 11: Writing Scripts and Batches 57012c11.qxd:WroxBeg 11/25/08 5:53 AM Page 329 Don’t worry if you don’t recognize some of the terms in a few of these. They will become clear in due time, and you will have this table or Appendix A to look back on for reference at a later date. The thing to remember is that there are sources you can go to in order to find out a whole host of information about the current state of your system and your activities. Variable Purpose Comments @@REMSERVER Used only in stored procedures. Returns the value of the server that called the stored procedure. Handy when you want the sproc to behave dif- ferently depending on the remote server (often a geographic location) from which it was called. Still, in this era of .NET, I would question whether anything needing this variable might have been better written using other functional- ity found in .NET. @@ROWCOUNT One of the most used system func- tions. Returns the number of rows affected by the last statement. Commonly used in non runtime error checking. For example, if you try to DELETE a row using a WHERE clause, and no rows are affected, then that would imply that something unexpected hap- pened. You can then raise an error manually. SCOPE_IDENTITY Similar to @@IDEN- TITY , but returns the last identity inserted within the current session and scope. Very useful for avoiding issues where a trigger or nested stored procedure has performed addi- tional inserts that have overwritten your expected identity value. @@SERVERNAME Returns the name of the local server that the script is running from. Can be changed by using sp_addserver and then restarting SQL Server, but rarely required. @@TRANCOUNT Returns the number of active transac- tions — essentially the transaction nest- ing level — for the current connection. A ROLLBACK TRAN statement decrements @@TRANCOUNT to 0 unless you are using save points. BEGIN TRAN increments @@TRANCOUNT by 1, COMMIT TRAN decrements @@TRANCOUNT by 1. @@VERSION Returns the current version of SQL Server as well as the date, processor, and O/S architecture. Unfortunately, this doesn’t return the information into any kind of structured field arrangement, so you have to parse it if you want to use it to test for specific information. Also be sure to check out the xp_msver extended stored procedure. 330 Chapter 11: Writing Scripts and Batches 57012c11.qxd:WroxBeg 11/25/08 5:53 AM Page 330 Using @@IDENTITY @@IDENTITY is one of the most important of all the system functions. An identity column is one where we don’t supply a value, and SQL Server inserts a numbered value automatically. In our example case, we obtain the value of @@IDENTITY right after performing an insert into the Orders table. The issue is that we don’t supply the key value for that table — it’s automatically created as we do the insert. Now we want to insert a record into the OrderDetails table, but we need to know the value of the primary key in the associated record in the Orders table (remember, there is a foreign key constraint on the OrderDetails table that references the Orders table). Because SQL Server generated that value instead of us supplying it, we need to have a way to retrieve that value for use in our dependent inserts later on in the script. @@IDENTITY gives us that automatically generated value because it was the last statement run. In this example, we could have easily gotten away with not moving @@IDENTITY to a local variable — we could have just referenced it explicitly in our next INSERT query. I make a habit of always moving it to a local variable, however, to avoid errors on the occasions when I do need to keep a copy. An example of this kind of situation would be if we had yet another INSERT that was dependent on the identity value from the INSERT into the Orders table. If I hadn’t moved it into a local variable, then it would be lost when I did the next INSERT, because it would have been overwritten with the value from the OrderDetails table, which, since OrderDetails has no identity column, means that @@IDENTITY would have been set to NULL. Moving the value of @@IDENTITY to a local variable also let me keep the value around for the statement where I printed out the value for later reference. Let’s create a couple of tables to try this out: CREATE TABLE TestIdent ( IDCol int IDENTITY PRIMARY KEY ); CREATE TABLE TestChild1 ( IDcol int PRIMARY KEY FOREIGN KEY REFERENCES TestIdent(IDCol) ); CREATE TABLE TestChild2 ( IDcol int PRIMARY KEY FOREIGN KEY REFERENCES TestIdent(IDCol) ); What we have here is a parent table — it has an identity column for a primary key (as it happens, that’s the only column it has). We also have two child tables. They each are the subject of an identifying rela- tionship — that is, they each take at least part (in this case all) of their primary key by placing a foreign 331 Chapter 11: Writing Scripts and Batches 57012c11.qxd:WroxBeg 11/25/08 5:53 AM Page 331 key on another table (the parent). So what we have is a situation where the two child tables need to get their key from the parent. Therefore, we need to insert a record into the parent first, and then retrieve the identity value generated so we can make use of it in the other tables. Try It Out Using @@IDENTITY Now that we have some tables to work with, we’re ready to try a little test script: /***************************************** ** This script illustrates how the identity ** value gets lost as soon as another INSERT ** happens ****************************************** */ DECLARE @Ident int; This will be a holding variable /* We’ll use it to show how we can ** move values from system functions ** into a safe place. */ INSERT INTO TestIdent DEFAULT VALUES; SET @Ident = @@IDENTITY; PRINT ‘The value we got originally from @@IDENTITY was ‘ + CONVERT(varchar(2),@Ident); PRINT ‘The value currently in @@IDENTITY is ‘ + CONVERT(varchar(2),@@IDENTITY); /* On this first INSERT using @@IDENTITY, we’re going to get lucky. ** We’ll get a proper value because there is nothing between our ** original INSERT and this one. You’ll see that on the INSERT that ** will follow after this one, we won’t be so lucky anymore. */ INSERT INTO TestChild1 VALUES (@@IDENTITY); PRINT ‘The value we got originally from @@IDENTITY was ‘ + CONVERT(varchar(2),@Ident); IF (SELECT @@IDENTITY) IS NULL PRINT ‘The value currently in @@IDENTITY is NULL’; ELSE PRINT ‘The value currently in @@IDENTITY is ‘ + CONVERT(varchar(2),@@IDENTITY); The next line is just a spacer for our print out PRINT ‘’; /* The next line is going to blow up because the one column in ** the table is the primary key, and primary keys can’t be set ** to NULL. @@IDENTITY will be NULL because we just issued an ** INSERT statement a few lines ago, and the table we did the ** INSERT into doesn’t have an identity field. Perhaps the biggest ** thing to note here is when @@IDENTITY changed - right after ** the next INSERT statement. */ INSERT INTO TestChild2 VALUES (@@IDENTITY); 332 Chapter 11: Writing Scripts and Batches 57012c11.qxd:WroxBeg 11/25/08 5:53 AM Page 332 How It Works What we’re doing in this script is seeing what happens if we depend on @@IDENTITY directly rather than moving the value to a safe place. When we execute the preceding script, everything’s going to work just fine until the final INSERT. That final statement is trying to make use of @@IDENTITY directly, but the preceding INSERT statement has already changed the value in @@IDENTITY. Because that statement is on a table with no identity column, the value in @@IDENTITY is set to NULL. Because we can’t have a NULL value in our primary key, the last INSERT fails: (1 row(s) affected) The value we got originally from @@IDENTITY was 1 The value currently in @@IDENTITY is 1 (1 row(s) affected) The value we got originally from @@IDENTITY was 1 The value currently in @@IDENTITY is NULL Msg 515, Level 16, State 2, Line 41 Cannot insert the value NULL into column ‘IDcol’, table ‘Accounting.dbo.TestChild2’; column does not allow nulls. INSERT fails. The statement has been terminated. If we make just one little change (to save the original @@IDENTITY value): /***************************************** ** This script illustrates how the identity ** value gets lost as soon as another INSERT ** happens ****************************************** */ DECLARE @Ident int; This will be a holding variable /* We’ll use it to show how we can ** move values from system functions ** into a safe place. */ INSERT INTO TestIdent DEFAULT VALUES; SET @Ident = @@IDENTITY; PRINT ‘The value we got originally from @@IDENTITY was ‘ + CONVERT(varchar(2),@Ident); PRINT ‘The value currently in @@IDENTITY is ‘ + CONVERT(varchar(2),@@IDENTITY); /* On this first INSERT using @@IDENTITY, we’re going to get lucky. ** We’ll get a proper value because there is nothing between our ** original INSERT and this one. You’ll see that on the INSERT that ** will follow after this one, we won’t be so lucky anymore. */ INSERT INTO TestChild1 VALUES (@@IDENTITY); PRINT ‘The value we got originally from @@IDENTITY was ‘ + CONVERT(varchar(2),@Ident); IF (SELECT @@IDENTITY) IS NULL PRINT ‘The value currently in @@IDENTITY is NULL’; 333 Chapter 11: Writing Scripts and Batches 57012c11.qxd:WroxBeg 11/25/08 5:53 AM Page 333 ELSE PRINT ‘The value currently in @@IDENTITY is ‘ + CONVERT(varchar(2),@@IDENTITY); The next line is just a spacer for our print out PRINT ‘’; /* This time all will go fine because we are using the value that ** we have placed in safekeeping instead of @@IDENTITY directly.*/ INSERT INTO TestChild2 VALUES (@Ident); This time everything runs just fine: (1 row(s) affected) The value we got originally from @@IDENTITY was 1 The value currently in @@IDENTITY is 1 (1 row(s) affected) The value we got originally from @@IDENTITY was 1 The value currently in @@IDENTITY is NULL (1 row(s) affected) Using @@ROWCOUNT In the many queries that we ran up to this point, it’s always been pretty easy to tell how many rows a statement affected — the Query window tells us. For example, if we run: USE AdventureWorks2008 SELECT * FROM Person.Person; then we see all the rows in Person, but we also see a count on the number of rows affected by our query (in this case, it’s all the rows in the table): (19972 row(s) affected) But what if we need to programmatically know how many rows were affected? Much like @@IDENITY, @@ROWCOUNT is an invaluable tool in the fight to know what’s going on as your script runs — but this time the value is how many rows were affected rather than our identity value. Let’s examine this just a bit further with an example: USE AdventureWorks2008; GO In this example, it was fairly easy to tell that there was a problem because of the attempt at inserting a NULL into the primary key. Now, imagine a far less pretty sce- nario — one where the second table did have an identity column. You could easily wind up inserting bogus data into your table and not even knowing about it — at least not until you already had a very serious data integrity problem on your hands! 334 Chapter 11: Writing Scripts and Batches 57012c11.qxd:WroxBeg 11/25/08 5:53 AM Page 334 DECLARE @RowCount int; Notice the single @ sign SELECT * FROM Person.Person; SELECT @RowCount = @@ROWCOUNT; This again shows us all the rows, but notice the new line that we got back: The value of @@ROWCOUNT was 19972 We’ll take a look at ways this might be useful when we look at stored procedures later in the book. For now, just realize that this provides us with a way to learn something about what a statement did, and it’s not limited to use SELECT statements — UPDATE, INSERT, and DELETE also set this value. Batches A batch is a grouping of T-SQL statements into one logical unit. All of the statements within a batch are combined into one execution plan, so all statements are parsed together and must pass a validation of the syntax or none of the statements will execute. Note, however, that this does not prevent runtime errors from happening. In the event of a runtime error, any statement that has been executed prior to the runtime error will still be in effect. To summarize, if a statement fails at parse-time, then nothing runs. If a statement fails at runtime, then all statements until the statement that generated the error have already run. All the scripts we have run up to this point are made up of one batch each. Even the script we’ve been analyzing so far this in chapter makes up just one batch. To separate a script into multiple batches, we make use of the GO statement. The GO statement: ❑ Must be on its own line (nothing other than a comment can be on the same line); there is an exception to this discussed shortly, but think of a GO as needing to be on a line to itself. ❑ Causes all statements since the beginning of the script or the last GO statement (whichever is closer) to be compiled into one execution plan and sent to the server independently of any other batches. ❑ Is not a T-SQL command, but, rather, a command recognized by the various SQL Server com- mand utilities (sqlcmd and the Query window in the Management Studio). A Line to Itself The GO command should stand alone on its own line. Technically, you can start a new batch on the same line after the GO command, but you’ll find this puts a serious damper on readability. T-SQL statements cannot precede the GO statement, or the GO statement will often be misinterpreted and cause either a parsing error or some other unexpected result. For example, if I use a GO statement after a WHERE clause: SELECT * FROM Person.Person WHERE ContactID = 1 GO If you look through the example, you might notice that, much as I did with @@IDENTITY, I chose to move the value off to a holding variable. @@ROWCOUNT will be reset with a new value the very next statement, so, if you’re going to be doing multiple activities with the @@ROWCOUNT value, you should move it into a safe keeping area. 335 Chapter 11: Writing Scripts and Batches 57012c11.qxd:WroxBeg 11/25/08 5:53 AM Page 335 the parser becomes somewhat confused: Msg 102, Level 15, State 1, Line 1 Incorrect syntax near ‘GO’. Each Batch Is Sent to the Server Separately Because each batch is processed independently, an error in one batch does not prevent another batch from running. To illustrate, take a look at some code: USE AdventureWorks2008; DECLARE @MyVarchar varchar(50); This DECLARE only lasts for this batch! SELECT @MyVarchar = ‘Honey, I’’m home ’; PRINT ‘Done with first Batch ’; GO PRINT @MyVarchar; This generates an error since @MyVarchar isn’t declared in this batch PRINT ‘Done with second Batch’; GO PRINT ‘Done with third batch’; Notice that this still gets executed even after the error GO If there were any dependencies between these batches, then either everything would fail — or, at the very least, everything after the point of error would fail — but it doesn’t. Look at the results if you run the previous script: Done with first Batch Msg 137, Level 15, State 2, Line 2 Must declare the scalar variable “@MyVarchar”. Done with third batch Again, each batch is completely autonomous in terms of runtime issues. Keep in mind, however, that you can build in dependencies in the sense that one batch may try to perform work that depends on the first batch being complete — we’ll see some of this in the next section when I talk about what can and can’t span batches. GO Is Not a T-SQL Command Thinking that GO is a T-SQL command is a common mistake. GO is a command that is only recognized by the editing tools (Management Studio, sqlcmd). If you use a third-party tool, then it may or may not support the GO command, but most that claim SQL Server support will. 336 Chapter 11: Writing Scripts and Batches 57012c11.qxd:WroxBeg 11/25/08 5:53 AM Page 336 When the editing tool encounters a GO statement, it sees it as a flag to terminate that batch, package it up, and send it as a single unit to the server — without including the GO. That’s right, the server itself has absolutely no idea what GO is supposed to mean. If you try to execute a GO command in a pass-through query using ODBC, OLE DB, ADO, ADO.NET, SqlNativeClient, or any other access method, you’ll get an error message back from the server. The GO is merely an indicator to the tool that it is time to end the current batch, and time, if appropriate, to start a new one. Errors in Batches Errors in batches fall into two categories: ❑ Syntax errors ❑ Runtime errors If the query parser finds a syntax error, processing of that batch is cancelled immediately. Since syntax checking happens before the batch is compiled or executed, a failure during the syntax check means none of the batch will be executed — regardless of the position of the syntax error within the batch. Runtime errors work quite a bit differently. Any statement that has already executed before the runtime error was encountered is already done, so anything that statement did will remain intact unless it is part of an uncommitted transaction. (Transactions are covered in Chapter 14, but the relevance here is that they imply an all or nothing situation.) What happens beyond the point of the runtime error depends on the nature of the error. Generally speaking, runtime errors terminate execution of the batch from the point where the error occurred to the end of the batch. Some runtime errors, such as a referential- integrity violation, will only prevent the offending statement from executing — all other statements in the batch will still be executed. This later scenario is why error checking is so important — we will cover error checking in full in our chapter on stored procedures (Chapter 12). When to Use Batches Batches have several purposes, but they all have one thing in common — they are used when something has to happen either before or separately from everything else in your script. Statements That Require Their Own Batch There are several commands that absolutely must be part of their own batch. These include: ❑ CREATE DEFAULT ❑ CREATE PROCEDURE ❑ CREATE RULE ❑ CREATE TRIGGER ❑ CREATE VIEW If you want to combine any of these statements with other statements in a single script, then you will need to break them up into their own batch by using a GO statement. 337 Chapter 11: Writing Scripts and Batches 57012c11.qxd:WroxBeg 11/25/08 5:53 AM Page 337 [...]... Mountain-100 Blac k, 44 3 2024.9940 0.0000 60 74.982000 AW0000 067 6 4 365 9 2007-10- 06 00:00:00.000 778 Mountain-100 Blac k, 48 1 2024.9940 0.0000 2024.994000 … … AW00000227 4 366 2 2007-10- 06 00:00:00.000 738 LL Road Frame - B lack, 52 1 178.5808 0.0000 178.580800 AW00000227 4 366 2 2007-10- 06 00:00:00.000 766 Road -65 0 Black, 6 0 3 419.4589 0.0000 1258.3 767 00 343 57012c11.qxd:WroxBeg 11/25/08 5:53 AM Page 344 Chapter... ‘AdventureWorks2008’ AccountNumber SalesOrderID OrderDate ProductID Name OrderQty UnitPrice TotalDiscount LineTotal - - - - - -AW0000 067 6 4 365 9 2007-10- 06 00:00:00.000 7 76 Mountain-100 Blac k, 42 1 2024.9940 0.0000 2024.994000 AW0000 067 6 4 365 9 2007-10- 06 00:00:00.000 777 Mountain-100 Blac k, 44 3 2024.9940 0.0000 60 74.982000... 1998- 06- 01 00:00:00.000 6 Miscellaneous Storage 0.0000 00 1998- 06- 01 00:00:00.000 7 Finished Goods Storage 0.0000 00 1998- 06- 01 00:00:00.000 10 Frame Forming 22.5000 96. 00 1998- 06- 01 00:00:00.000 20 Frame Welding 25.0000 108.00 1998- 06- 01 00:00:00.000 30 Debur and Polish 14.5000 120.00 1998- 06- 01 00:00:00.000 40 Paint 15.7500 120.00 1998- 06- 01 00:00:00.000 45 Specialized Paint 18.0000 80.00 1998- 06- 01... 970 960 950 940 930 920 910 900 Name -Mountain-500 Black, 42 Mountain-400-W Silver, 38 Touring-2000 Blue, 46 Touring-3000 Blue, 62 ML Crankset HL Road Pedal HL Mountain Tire LL Mountain Frame - Silver, 52 HL Mountain Seat/Saddle LL Touring Frame - Yellow, 50 ListPrice 539.99 769 .49 1214.85 742.35 2 56. 49 80.99 35.00 264 .05 52 .64 333.42 Marked Up Price -593.989 8 46. 439 13 36. 335... ListPrice 539.99 769 .49 1214.85 742.35 2 56. 49 80.99 35.00 264 .05 52 .64 333.42 Marked Up Price -593.989 8 46. 439 13 36. 335 8 16. 585 282.139 89.089 38.50 290.455 57.904 366 . 762 New Price 593.9500 8 46. 4900 13 36. 4900 8 16. 7500 282.4900 89.4900 38.7500 290.4900 57.9500 366 .9500 (10 row(s) affected) Look these over for a bit, and you’ll see that the results match what we were expecting What’s more,... as well as a quick and dirty way to capture a text file sqlcmd replaces the older osql osql is still included with SQL Server for backward compatibility only An even older command-line utility — isql — is no longer supported The syntax for running sqlcmd from the command line includes a large number of different switches, and looks like this: sqlcmd [ { { -U [ -P ] } | –E } ]... you only the remainder — therefore, 16 % 4 = 0 (4 goes into 16 evenly), but 16 % 5 = 1 ( 16 divided by 5 has a remainder of 1) In the example, since we’re dividing by 10, using the modulus is giving us the last digit of the number we’re evaluating Let’s see what we got with this: SalesOrderID Last Digit 43793 3 51522 2 57418 8 43 767 7 51493 3 72773 3 437 36 6 51238 8 53237 7 43701 1 (10 row(s)... Chapter 11: Writing Scripts and Batches AW00000227 0.0000 4 366 2 2007-10- 06 00:00:00.000 1 874.794000 755 Road-450 Red, 60 874.7940 (51 rows affected) C:\> How It Works We started out by bundling the SQL commands we would need into a single script — first, the USE command, and then the actual SELECT statement We then executed our statement using sqlcmd The –U and –P commands provided the login username... brevity): UnitMeasureCode BOX BTL C … … PC PCT PT Name -Boxes Bottle Celsius ModifiedDate 1998- 06- 01 00:00:00.000 1998- 06- 01 00:00:00.000 1998- 06- 01 00:00:00.000 Piece Percentage Pint, US liquid 1998- 06- 01 00:00:00.000 1998- 06- 01 00:00:00.000 1998- 06- 01 00:00:00.000 (38 row(s) affected) The Gotchas of EXEC Like most things that are of interest, using EXEC is not without... however, we’ve said we want our results to a text file, so we’ll need to add some extra parameters to our sqlcmd command line this time to tell SQL Server where to put the output: C:\>sqlcmd -UMyLogin -PMyPass -iYesterdaysOrders .sql -oYesterdaysOrders.txt There won’t be anything special or any fanfare when sqlcmd is done running this — you’ll simply get your Windows drive prompt again (C:\ most likely), but . 2024.994000 … … AW00000227 4 366 2 2007-10- 06 00:00:00.000 738 LL Road Frame - B lack, 52 1 178.5808 0.0000 178.580800 AW00000227 4 366 2 2007-10- 06 00:00:00.000 766 Road -65 0 Black, 6 0 3 419.4589 0.0000 1258.3 767 00 343 Chapter. 2024.9940 0.0000 2024.994000 AW0000 067 6 4 365 9 2007-10- 06 00:00:00.000 777 Mountain-100 Blac k, 44 3 2024.9940 0.0000 60 74.982000 AW0000 067 6 4 365 9 2007-10- 06 00:00:00.000 778 Mountain-100 Blac k,. way to capture a text file. sqlcmd replaces the older osql. osql is still included with SQL Server for backward compatibility only. An even older command-line utility — isql — is no longer supported. The

Ngày đăng: 09/08/2014, 14:21

Tài liệu cùng người dùng

Tài liệu liên quan