This is the third in a series of articles examining some basic concepts in Sql Server. Basic Anatomy of Sql Server, part I - What is a stored procedure? Basic Anatomy of Sql Server, part II - The unit test as part of the database What does deferred name resolution really mean? Essentially part II answered the often asked question, "why can't I SELECT from an sql stored procedure?". Because there is nothing to select from. There is an absence of the concept of a table type in sql which is necessary to realize a table. Now we ask another simple question. How is it D4 can understand the assignment of a table and a test of equality between two tables just as t-sql understands the same things between numbers: var aNewTable:= GroupByShipCountry(1) ; //Assign the table for employee 1 based //on the D4 operator from Part I to //aNewTable. var Test1:= aNewTable=GroupByShipCountry(1);//Are the two tables the same? Yes. //Now change aNewTable with an update. update (aNewTable adorn{key{Cnt,MinFrt}}) set {MinFrt:=$0.00} where ((Cnt<11) and (MinFrt<$2.50)); var Test2:= aNewTable=GroupByShipCountry(1); //Now are the two tables the same? No. select row{Test1 TestofEquality_before_Update, Test2 TestofEquality_after_Update}; TestofEquality_before_Update TestofEquality_after_Update ---------------------------- --------------------------- True False And how is it that t-sql can do with numbers what D4 does with tables: DECLARE @X Integer, @Y Integer; SET @X=5; SET @Y=10; if @X=@Y print '@X=@Y' else print '@X!=@Y' @X!=@Y The answer is obvious and simple. We can only assign values and make comparisons with variables. And we cannot do anything with a variable unless it is typed. The unit in 'unit test' is the awareness of the database of a table as a variable. To say the idea of a unit test is part of the database is to recognize that a table as a variable and its type is the only way to distinguish one table from another. The unit is the same for distinguishing among numbers and strings and tables. And it is this basic unit that sql server lacks for tables. Sql server bol describes deferred name resolution as: 'This process is called deferred name resolution because table objects referenced by the stored procedure need not exist when the stored procedure is created, but only when it is executed.' What is this really a statement of? When t-sql parses these statements: IF @X=5 PRINT '@X=5' ELSE PRINT '@X!=5' it returns the error: Must declare the scalar variable "@X". In D4 when this statement is parsed: select aNonExistentTable; it returns the same error as t-sql: Unknown identifier "aNonExistentTable". These errors are the same. Each system is aware that a variable has not been defined with a type. There is the intent at parse in each system to check that an identifier (@X, aNonExistentTable) has been appropriately defined. And where the identifier is recognized as a variable and nothing else. This intent has nothing to do with the context that surrounds the identifier. And it is this intent that lies at the heart of a relational database. A statement of just the variable results in the same error. t-sql D4 ----- ------- @X aNonExistentTable; Must declare the scalar variable "@X"., Unknown identifier "aNonExistentTable". Now these are all examples of so called t-sql deferred name resolution, there is no error raised on parsing only execution: SELECT * FROM aNonExistentTable SELECT aNonExistentTable IF aNonExistentTable=anotherNonExistentTable print 'YES' CREATE PROCEDURE theTableDoesNotExist AS SELECT * FROM aNonExistentTable Given that there is computer science and not one computer science for t-sql and one for everything else, how is it that these statements can be parsed without error? Because at parse-time if there is no concept of an identitifier as a variable there is only context to check, syntax. Absence the idea of a variable there is nothing to resolve a database object to, hence it is as if these objects/identifiers do not exist at parse-time. How bol describes deferred name resolution is a consequence of working with tables in a variable-less system. It is a price paid, in terms of sacrifice of integrity and sacrifice in management of table objects, by the developer for working in a type-less and variable-less and therefore a non-relational database. Explanations like deferred name resolution are quite common in sql but they are all just different sides of the same central issue. For example there is the issue of impedance mismatch. This is just an expression of the inherent difficulty of communication between a client, which understands what a variable is, and an sql server which does not. And finally there is the idea of relational division. Since comparisons require variables sql forces an inference to be made about a comparison of table(s) it cannot do. It forces the use of constructs that are obtuse and complex compared to simple and direct comparisons that can be made in a relational database where the table exists as a variable. (And now you know why you can't pass a table as a parameter. There is no table variable to pass ☺ . The history of sql is a history of a language trying to exist outside the mainstream of the rest of IT. It's long overdue that application development use a database that has the same foundation as other programming languages. And that kind of database is relational. Basic Anatomy of Sql Server, part I What is a stored procedure? Basic Anatomy of Sql Server, part II The unit test as part of the database. Basic Anatomy of Sql Server, part III What does deferred name resolution really mean? Some related articles of interest: All tables are typed variables A table as a parameter Reusable procedures
Dataphor SQL RAC (Relational Application Companion)
A site of hope for those looking for a true relational database system
- a one-one requirement constraint with dataphor (1)
- anatomy of sql server part I - what is a stored procedure (1)
- anatomy of sql server part II - the unit test as part of the database (1)
- anatomy of sql server part III - what does deferred name resolution really mean (1)
- censoring sql posts (1)
- creating an opposite constraint in dataphor (1)
- dataphor (2)
- Dataphor (7)
- dataphor # 13 a table as a parameter (1)
- dataphor - download and start working with it (1)
- dataphor - fixed sized word segments (1)
- dataphor # 10 sql mythology (1)
- dataphor # 11 string differences (1)
- dataphor # 12 trimming a string (1)
- dataphor # 14 sql the meaning of Update..From (1)
- dataphor # 15 views with substance (1)
- dataphor # 16 inclusive vs exclusive solutions (1)
- dataphor # 17 a visual look at ranking queries (1)
- dataphor # 18 data scrubbing using lists (1)
- dataphor # 19 create intervals over strings (1)
- dataphor # 20 browsing an sql window (1)
- dataphor # 21 an example of relational division (1)
- dataphor # 22 reusable procedures (1)
- dataphor # 23 repley to Michel (1)
- dataphor # 24 basics of the table type (1)
- dataphor # 25 extending the dense rank function (1)
- dataphor # 26 query a hierarchy with explode (1)
- dataphor # 27 combine strings with Split and Concat (1)
- dataphor # 28 constants and variables or sql and D4 (1)
- dataphor # 29 another example of relational division (1)
- dataphor #1 introduction (1)
- dataphor #2 splitting strings (1)
- dataphor #3 string concatenation (1)
- dataphor #4 comment (1)
- dataphor #5 comment (1)
- dataphor #6 formal definition (1)
- dataphor #7 sql: table this (1)
- dataphor #8 list to table (1)
- dataphor #9 table constraints (1)
- dataphor creating lists in a query (1)
- extracting numbers from a string with dataphor (1)
- jeff modens dynamic crosstabs for sql server (1)
- linq to sql the what and why (1)
- linq to sql as a window of opportunity to sql users (1)
- linq to sql should be important to sql users (1)
- linq to sql vs. older 4GL attempts (1)
- listing missing table item (1)
- Multiple cascade paths to the same table (1)
- RAC (4)
- RAC #1 comment (1)
- RAC #2 example (1)
- RAC #3 finding the Nth number in a string (1)
- RAC #4 Sql Server 2005 ranking functions vs. Rac ranking (1)
- sorting a delimited string by its numerical string parts (1)
- sql an example of extreme implicit conversions (1)
- sql can't handle complicated cascading updates (1)
- sql CTE should be a variable not a value (1)
- sql dense rank for identifying consecutive runs (1)
- sql is there really a table variable (1)
- sql ranking functions explained by relational types (1)
- sql server triggers are best set based (1)
- sql the idea of using substring to simulate lists (1)
- sql the undefined trigger in Sql Server (1)
- sql vs relational on tables (1)
- sql what the sql CTE covers up (1)
- types and procedures (1)
Tuesday, November 06, 2007
Basic Anatomy of Sql Server, part III
Basic Anatomy of Sql Server, part II
This is the second in a series of articles examining some basic concepts in Sql Server. Basic Anatomy of Sql Server, part I - What is a stored procedure? The unit test as part of the database. If Sql Server is operating with the genie in the bottle what would a system look like with the genie out of the bottle. Here we take the sql procedure dbo.GroupByShipCountry from part I and use it in a system that takes the definition beyond the statements that define it What does such a system look like and what exactly can we expect such a system to be aware off. Here we use a system called D4, a system with a kinship to sql but fundamentally different. For reference we repeat the sql procedure dbo.GroupByShipCountry from part I: CREATE PROCEDURE dbo.GroupByShipCountry @Employee Integer AS SELECT ShipCountry,Count(*) Cnt,Min(Freight) MinFrt,Max(Freight) MaxFrt FROM Orders WHERE EmployeeID=@Employee GROUP BY ShipCountry Here is the sql procedure as it would be defined in D4: create operator GroupByShipCountry (Employee:Integer): table{ShipCountry:String,Cnt:Integer,MinFrt:Money,MaxFrt:Money} begin result:= Orders where EmployeeID=Employee group by {ShipCountry} add{Count() Cnt,Min(Freight) MinFrt,Max(Freight) MaxFrt} ; end; While the differences in syntax between the two languages are beyond the scope of this article it should be obvious that the definition of result in D4 is similar to the SELECT statement in the sql procedure. Semantically they are identical. The sql procedure and D4 operator each have a parameter to define an employee. The obvious difference between the two is the construct: table{ShipCountry:String,Cnt:Integer,MinFrt:Money,MaxFrt:Money} in the beginning of the D4 operator. This says the result of the operator is a table with the specific type of: ShipCountry:String,Cnt:Integer,MinFrt:Money,MaxFrt:Money Lets execute the operator with syntax as close as possible to the sql sp: Execute('GroupByShipCountry(8)'); What is returned? Absolutely nothing. A D4 operator can also be specified with just its name and input parameter(s) values. So lets try: GroupByShipCountry(8); What we get with this is the following system message: Possibly incorrect use of expression as a statement. What is going on here? Why doesn't the operator display something like the sql procedure? Lets go back to the sql procedure. At run-time the procedure is still only a bunch of t-sql statements so 'execute' is appropriate as in 'execute these statements'. But if the D4 operator is a table at run-time it makes no sense to 'execute' a table! So lets try to use the 'table' in its appropriate context. In sql we would simply say: SELECT * FROM <table> Which corresponds to the D4 statement: select <table>; We now substite the D4 operator with an input parameter for the '<table>': select GroupByShipCountry(8); And now we get a similar display as the sql procedure: ShipCountry Cnt MinFrt MaxFrt ----------- --- ------ ------- Argentina 3 $0.33 $217.86 Austria 5 $25.22 $353.07 Brazil 9 $5.32 $110.87 . Venezuela 9 $4.41 $141.06 Clearly the D4 system is aware of two distinct things. The definition of the operator, its statements, and then at run-time a table. And now it should also be clear that sql is only aware of statements. How does D4 guarantee that the GroupByShipCountry(8) table will be of the type: table{ShipCountry:String,Cnt:Integer,MinFrt:Money,MaxFrt:Money} Because that is the only type of table possible. If the object (table) exists at run-time it can only be of this type. And if the table is derived it must have come from compatible datatypes. Another way of saying this is that a system can distinguish between tables or it can support implicit conversion. If the system can be aware of a table type it cannot support implicit conversion. Only explicit conversion, an intervention on the part of the user, can be consistent with a table type. Sql supports implicit conversion and therefore cannot tell one table from another. Sql therefore cannot guarantee the integrity of a table. It is object (table) types that offers integrity. For sql to support implicit conversion means it has no concept of a table of a specific type as an object. For example, this sql illustrates conversion, from a money type to an integer type: CREATE TABLE TestConv (ShipCountry varchar(15),Cnt int,MinFrt int,MaxFrt int, PRIMARY KEY (ShipCountry)) GO INSERT TestConv (ShipCountry,Cnt,MinFrt,MaxFrt) EXEC GroupByShipCountry 8 It also undermines the integrity of each table and therefore the database. This is what would happen in a D4 operator if a table with incompatible datatypes with the source was defined: create operator GroupByShipCountryI (Employee:Integer): table{ShipCountry:String,Cnt:Integer,MinFrt:Integer,MaxFrt:Integer} begin result:= Orders where EmployeeID=Employee group by {ShipCountry} add{Count() Cnt,Min(Freight) MinFrt,Max(Freight) MaxFrt} ; end; The error returned is a compile error (as opposed to run-time): Cannot convert a value of type "System.Money" to a value of type "System.Integer". Not only does the database offer integrity through type compatibility but the entire framework is exposed to the developer for use as part of the application itself. A value can be checked for its type with an operator that accepts any type (a generic). We can make a comparison of the value to a specific type which returns a boolean value of True/False. create operator WhatIsIt(aValue:generic):String begin result:= if aValue is String then 'String' else if aValue is Integer then 'Integer' else if aValue is Money then 'money' else 'Some other type'; end; select WhatIsIt((GroupByShipCountry(8) adorn{key{ShipCountry}})['Canada'].MaxFrt); money We have created the GroupByShipCountry(8) table with a key on ShipCountry and directly accessed the row where ShipCountry is 'Canada' and extracted the value of MaxFrt to use as the argument to operator WhatIsIt. The type of that value is money. And we can extend the same type recognition logic to a table. var TryTable:Boolean:=false; if WhatIsIt(GroupByShipCountry(8))='Some other type' then TryTable:=GroupByShipCountry(8) is generic table; select TryTable; True We have used the GroupByShipCountry(8) table as the argument to the WhatIsIt procedure. The procedure returned the string 'Some other type' and in the if statement we asked if the GroupByShipCountry(8) table is a generic table. In other words we are testing if GroupByShipCountry(8) represents some specific type of table. And we can go from a generic type of table to a specific type of table: var TryTable:Boolean:=false; if WhatIsIt(GroupByShipCountry(8))='Some other type' then if (GroupByShipCountry(8) is generic table) then TryTable:= (table of typeof(GroupByShipCountry(8)){}) is table{ShipCountry:String,Cnt:Integer,MinFrt:Money,MaxFrt:Money}; select TableDee add{TryTable Is_GroupByShipCountry8_this_table_type}; Is_GroupByShipCountry8_this_table_type -------------------------------------- True After determining that GroupByShipCountry(8) is some type of table we have asked the question, is the type of the GroupByShipCountry(8) table the specific type represented by the pairs of columns and their datatypes of: ShipCountry:String, Cnt:Integer, MinFrt:Money, MaxFrt:Money The statement: table of typeof(GroupByShipCountry(8)){} extracts just the type information from GroupByShipCountry(8) as information independent from the values in the rows of the table. var TryTable:Boolean:=false; if WhatIsIt(Orders)='Some other type' then if (Orders is generic table) then TryTable:= (table of typeof(Orders){}) is table{ShipCountry:String,Cnt:Integer,MinFrt:Money,MaxFrt:Money}; select TableDee add{TryTable Is_GroupByShipCountry8_this_table_type}; Is_GroupByShipCountry8_this_table_type -------------------------------------- False We have confirmed that the Orders table is not the same particular type of table as GroupByShipCountry(8). And finally we show the ability to make a functional test rests on the ability to make a unit test: var C:Boolean:=True; if ( table of typeof(GroupByShipCountry(1)){} is table{ShipCountry:String,Cnt:Integer,MinFrt:Money,MaxFrt:Money} ) and ( table of typeof(GroupByShipCountry(8)){} is table{ShipCountry:String,Cnt:Integer,MinFrt:Money,MaxFrt:Money} ) then if GroupByShipCountry(1)<>GroupByShipCountry(8) then C:=False; select TableDee add{C Are_Tables_of_Same_Type_the_Same}; Are_Tables_of_Same_Type_the_Same -------------------------------- False We have first verified that the two tables are the same type of table and then asked if the two tables are the same. In other words, we can only test the equality of two tables (they have the same set of rows) if they are the same table type just as we can only compare two numbers or two dates. It is obviously nonsensical and illogical to compare two objects of different types. (I note in passing that the fact that an sql database is aware of individual columns and their datatypes is fundamentally different from being aware that such pairs can be encapsulated to define a type of table. It is the notion of encapsulation that defines a type (table) independent of any column(s) as a scalar type that sql is aware of. Any place in t-sql that a table definition appears ie. CREATE TABLE statement, a table valued function or a table variable (a confusing and misleading construct) whatever this definition is the important point is this definition does not signify a table type). Can we make a test of equality between the sql procedure dbo.GroupByShipCountry and the D4 operator GroupByShipCountry? As just previously shown we can easily make such a functional test if both are recognized as table objects. We can elevate the mere t-sql statements of dbo.GroupByShipCountry by bringing in the statements to D4 with a pass-thru query. The following D4 query displays the same result as executing the procedure in Sql Server. select SQLQuery('Execute dbo.GroupByShipCountry 1'); ShipCountry Cnt MinFrt MaxFrt ----------- --- ------- -------- Argentina 1 63.7700 63.7700 Austria 5 74.6000 351.5300 Belgium 1 29.5900 29.5900 . Venezuela 8 0.9300 148.6100 Is the display a 'result set', as in Sql Server, or a table in D4? Let us apply a unit test to confirm or reject whether it is a table in D4: select table of typeof(SQLQuery('Execute dbo.GroupByShipCountry 1')){} is generic table; True which confirms that D4 is aware of the dbo.GroupByShipCountry t-sql result set as an object of some type of table at run-time. We can now try to test the equality of the two tables assuming they are of the same specific type. The following query should return a boolean, either True or False, on the equality (sameness) of the tables: select GroupByShipCountry(1) = SQLQuery('Execute dbo.GroupByShipCountry 1') ; But on checking the query at 'compile', two errors are returned: Cannot convert a value of type "System.Decimal" to a value of type "System.Money". No signature for operator "iCompare" matches the call signature "(System.Money, System.Decimal)". Both errors indicate a type mismatch. It is clear we cannot test for equality because the two tables are of different types. We know the specific type of table for the D4 operator. Let us go back to the unit test to find out what type of table is the sql procedure: var TypeofTable:=table of typeof(SQLQuery('Execute dbo.GroupByShipCountry 1')){}; var TypeString:String; if TypeofTable is table{ShipCountry:String,Cnt:Integer,MinFrt:Decimal,MaxFrt:Decimal} then TypeString:='table{ShipCountry:String,Cnt:Integer,MinFrt:Decimal,MaxFrt:Decimal}' else if TypeofTable is table{ShipCountry:String,Cnt:Integer,MinFrt:Money,MaxFrt:Money} then TypeString:='table{ShipCountry:String,Cnt:Integer,MinFrt:Money,MaxFrt:Money}' else TypeString:='A type where MinFrt and MaxFrt are differnt than Decimal and Money'; select TypeString; table{ShipCountry:String,Cnt:Integer,MinFrt:Decimal,MaxFrt:Decimal} Now we understand what's happening. The money datatype in Sql Server is really a decimal type significant to 4 digits. The money type in D4 is a totally different type. When D4 interacts with a server table it reads money for datatype. When the sql procedure is brought into D4 as a table the MinFrt and MinFrt are interpreted as decimal (and this also accounts for the different displays of the server procedure and the D4 operator). To compare the two tables we have to make an explicit conversion. We can convert the server decimal types to D4 money types. select GroupByShipCountry(1) = (SQLQuery('Execute dbo.GroupByShipCountry 1') {ShipCountry,Cnt,ToMoney(MinFrt) MinFrt,ToMoney(MaxFrt) MaxFrt}); True Clearly, the idea of a unit test in D4 can be used by a developer to provide a programmable form of integrity within an application. A database that recognizes, supports and exposes a unit test for a table type is an objects database. An objects database that has an algebra for tables is a relational database. D4 is a relational database. Basic Anatomy of Sql Server, part I What is a stored procedure? Basic Anatomy of Sql Server, part II The unit test as part of the database. Basic Anatomy of Sql Server, part III What does deferred name resolution really mean? Some related articles of interest: All tables are typed variables A table as a parameter Reusable procedures
Monday, November 05, 2007
Basic Anatomy of Sql Server, part I
This is the first in a series of articles examining some basic concepts in Sql Server. What is a stored procedure? This example uses Sql Server 2005 and the Northwind sample database. The following is a very simple stored procedure that returns some aggregates from the Orders table for a given EmployeeID value supplied as an input parameter: CREATE PROCEDURE dbo.GroupByShipCountry @Employee Integer AS SELECT ShipCountry,Count(*) Cnt,Min(Freight) MinFrt,Max(Freight) MaxFrt FROM Orders WHERE EmployeeID=@Employee GROUP BY ShipCountry After executing the above script to create the procedure we ask a most simple question: what is it? What exactly is Sql Server aware of regarding this procedure? We can take as the starting point the definition of a generic procedure. Bol states: 'A stored procedure is a saved collection of Transact-SQL statements ... that can take and return user-supplied parameters.' We can simply ask is there any indication that the server is aware of anything that rises above the level of the t-sql statements defining the procedure. Should we expect to find such thing? We do know that the server recognizes the object Orders in sys.objects as a USER_TABLE. And if we execute the query: SELECT * FROM Northwind.INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='Orders' we know the server recognizes the columns and their datatypes for the Orders table. We know the server recognizes that the procedure depends on three columns from the Orders table: SELECT object_name(referenced_major_id) Dependent,COLUMN_NAME FROM sys.sql_dependencies A CROSS APPLY (SELECT B.COLUMN_NAME FROM Northwind.INFORMATION_SCHEMA.COLUMNS B WHERE B.TABLE_NAME=object_name(A.referenced_major_id) and A.referenced_minor_id=B.ORDINAL_POSITION) C WHERE object_id=object_id('dbo.GroupByShipCountry') Dependent COLUMN_NAME --------- --------------- Orders EmployeeID Orders Freight Orders ShipCountry We know the procedure works if we execute it: Execute dbo.GroupByShipCountry 8 It retrieves, in the precise wording of bol, 'rows and columns from the Orders table' which are collectively termed a 'result set'. (We will return to the server Orders table and result set term in later articles). ShipCountry Cnt MinFrt MaxFrt --------------- ----------- --------------------- --------------------- Argentina 3 .3300 217.8600 Austria 5 25.2200 353.0700 Brazil 9 5.3200 110.8700 . Venezuela 9 4.4100 141.0600 When we look for specific information about the procedure itself with the sp_help system procedure we see that @Employee is listed as a parameter. The information from sys.objects confirms that the procedure is recognized and it indeed is a definition of a stored procedure: SELECT name,object_id,type,type_desc FROM sys.objects WHERE object_id = OBJECT_ID('dbo.GroupByShipCountry') AND type='P' name object_id type type_desc -------------------- ----------- ---- ---------------------- GroupByShipCountry 1551396646 P SQL_STORED_PROCEDURE In summary there does not exist any evidence that the server is aware of any information not contained in the collection of t-sql statements that define it. In fact the server does not exhibit any information about three of the four columns (Cnt, MinFrt, MaxFrt) that are displayed when it is executed. This leaves us in a very unsteady state. At the very most the server is performing an operation which is not exposed to us to interact with, at the very least it is performing an operation that it is itself unaware of. This situation makes it impossible to look to the server for any characterization of the procedure towards answering the question of what this procedure really is. It is up to us to infer what the procedure is. With the consequence that we can not rely on the server to either characterize what the procedure really is nor depend on the server to guarantee the integrity of the operation. This void in the server must be filled. The unit test Is it fair to say that the failure of the server necessitates a unit test? We can more clearly see the answer to this question if we can clearly see just what is the nature of a unit test that clarifies just what this procedure is. An insightful perspective on the unit test is offered in the excellent article: 'Using unit and functional tests in the development process' by Jeff Canna www.ibm.com/developerworks/library/j-test.html The author writes: 'Many times the development of a system is likened to the building of a house. Unit testing is analogous to a building inspector visiting a house's construction site. He is focused on the various internal systems of the house, the foundation, framing, electrical, plumbing, and so on. He ensures (tests) that the parts of the house will work correctly and safely, that is, meet the building code.' He goes on to say: 'Unit tests tell a developer that the code is doing things right; functional tests tell a developer that the code is doing the right things.' The author is clearly expressing the idea that a unit test is something the goes toward confirming something that is of a very basic nature. Something that is the basis on which any other functional tests are based on. So what sort of unit can we use to confirm the 'the code is doing things right'? And will it answer the question of what the procedure really is? The sort of unit test we are looking for is approached by the insight offered in another excellent article: 'Close those Loopholes - Testing Stored Procedures' by Alex Kuznetsov and Alex Styler www.simple-talk.com/content/print.aspx?article=426 With reference to a procedure very similar to dbo.GroupByShipCountry the authors write: 'Suppose you need to write a unit test for that stored procedure. You might want to verify that your result set has correct structure (correct column names and types)...' And further: 'When you set up your unit test, your result set (or result sets, if your stored procedure returns several ones) is saved into an XML file. All the information necessary for further comparison is saved column names, column types, and the data...When you run your unit test, the result set is compared against the saved XML file. Everything is compared - column names, column types, and all the data.' The idea of verifying that the 'result set has correct structure (correct column names and types).' What the authors refer to as 'correct structure' is what we are really searching for. A test for the correct structure is exactly what the server lacks. And what is this structure that is based on pairs of columns and their datatypes? For it is the columns of this structure that are displayed by executing the procedure: ShipCountry Cnt MinFrt MaxFrt ----------- --- ------ ------ which are based on the structure of column/datatype: ShipCountry:varchar, Cnt:Integer, MinFrt:Money, MaxFrt:Money It is these pairs that define a specific type, a specific type of table. And this simple concept allows us to finally answer the question of what the stored procedure really is: a table. The unit test is a test confirming the specific type of the table returned. What the server calls a 'result set' (and not a 'table') is based on the fact that there does not exist the concept of a type for a table. There does not exist a way for the server to distinguish one table from another. It is the non-existence of this concept in the server that necessitates this most basic unit (test) being made the responsibility of the user. This is the huge hole in sql that must be filled by the user, a role that the database should perform is lost and with it the integrity that it should offer. In part II we show and describe a database system that takes the definition of a stored procedure beyond the statements that define it. Basic Anatomy of Sql Server, part I What is a stored procedure? Basic Anatomy of Sql Server, part II The unit test as part of the database. Basic Anatomy of Sql Server, part III What does deferred name resolution really mean? Some related articles of interest: All tables are typed variables A table as a parameter Reusable procedures
Subscribe to:
Posts (Atom)