The SqlWindow example involves populating tables thru a form that is derived by the Dataphor frontend server. The fact that the form is derived by the system services is a key concept in Dataphors ability to be a vehicle of rapid application development. The example covers a wide range of Dataphor capabilities that are used in application development. Concepts covered include the presentation layer, metadata, forms, event handlers, references, constraints, cursors and views as well as many other programming aspects of D4. What does it do Based on user input several tables are populated and can be viewed thru a form. Complete editing is available. The example uses Sql Server 2005 as the data respository (device). Requirements The application was developed using Dataphor v2677 and MS Sql Server 2005 sp1. Except for an sql stored procedure (which is not required) the example should run on any version of Sql Server or any other database for that matter. Download the demo Everything needed to run the demo including screenshots and setup scripts for tables, views, constraints, references, operators etc. can be download @ http://www.geocities.com/d4tosql/D4/sqlwindowexample.zip All scripts for Dataphor objects are documented with comments that should help explain the what and why of the code. Screenshots You can view screenshots of the demo here and here. View a D4 operator used here. What is the context of the example Two different methods are used to compute cumulative aggregates that are based on the concept of an sql window as outlined in the Sql-99 standard. The sql window is intended to simplify and make more efficient the computing of cumulative sums. While an sql window is available in DB2 and Oracle, it is not in Sql Server. The two methods used in the example simulate two different conceptual approaches to deriving cumulative/running sums. Who is this intended for For anyone interested in exploring in greater detail key programming aspects of Dataphor used in application development. There are many operators and other Dataphor objects to go thru. The code is relatively straightforward and easy to follow. The example can also be used as a demo for those not familiar with Dataphor. The setup script will do everything required to run the example. It is only necessary to have a library using Sql Server as the device. The form in this example is 'completely' derived by the frontend server using metadata supplied in various places. Even without a customized form entering appropriate data is very easy and straightforward. If an entry is invalid and violates a constraint the custom error message(s) fully describe how to correct the entry. A modified form with menus will be available in the future. Sql background for computing cumulative/running sums Traditionally computing a running sum involved a subquery with an inequality comparision. Given the sample table Stocks, the following sql query will compute running sums for each Stock in ascending order of QTime. The query can be run from Dataphor using the SQLQuery operator to pass it directly to Sql Server. The use of the row_number() function allows the ranks to be easily compared. select SQLQuery (' select A.Stock,A.PRank,A.QTime,A.Quote, (select Sum(B.Quote) from (select Stock,QTime,Quote, row_number()over(Partition by Stock Order by QTime,Quote) as PRank from Stocks) as B where B.Stock=A.Stock and B.PRank<=A.PRank) as YSum from (select Stock,QTime,Quote, row_number()over(Partition by Stock Order by QTime,Quote) as PRank from Stocks) as A '); Stock PRank QTime Quote YSum -------- ----- ---------------------- ----- ---- IBM 1 4/3/2006 2:47:00 PM 107 107 IBM 2 7/16/2006 7:39:00 AM 157 264 IBM 3 8/26/2006 6:24:00 PM 125 389 IBM 4 9/8/2006 1:11:00 PM 171 560 IBM 5 11/22/2006 12:01:00 PM 155 715 IBM 6 2/2/2007 12:03:00 AM 171 886 IBM 7 4/13/2007 3:30:00 AM 219 1105 IBM 8 10/18/2007 12:12:00 AM 133 1238 IBM 9 12/25/2007 10:48:00 AM 139 1377 MS 1 3/17/2006 7:12:00 PM 257 257 . This method is similar to computing ranks as discussed here and is also just as inefficient. The impact of the subquery is more compelling when it is expressed as a table. The following query gives the counts for the stocks: select Stocks group by {Stock} add{Count() Cnt}; Stock Cnt -------- --- IBM 9 MS 11 MySQL_AB 12 Oracle 11 Sybase 9 For a table to represent the subquery it must be able to accommodate the maximum number of rows in a Stock. Based on MySQL_AB, we need at least 12 rows. Consider the following D4 batch which populates a table assuming the current row is the 6th row (PRank=6) in the 'IBM' Stock partition. The table represents all rows up to and including the 6th row. Non-existent rows, thru the use of IfNil, are given a PRank and nil (null) value for Quote. A sum of the Quote values from the table would represent the running sum up thru the 6th rank (PRank) for the 'IBM' Stock. //Shows table based on table var S for a particular Stock and PRank. var S:=SQLQuery('select Stock,QTime,Quote, row_number()over(Partition by Stock Order by QTime,Quote) as PRank from Stocks') {Stock,PRank,Quote} ; var Stock:='IBM'; var PRank:=6; select table { IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-11],row{-11 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-10],row{-10 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-9], row{ -9 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-8], row{ -8 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-7], row{ -7 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-6], row{ -6 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-5], row{ -5 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-4], row{ -4 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-3], row{ -3 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-2], row{ -2 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-1], row{ -1 PRank,nil Stock,nil Quote}), (S adorn{key{Stock,PRank}})[Stock,PRank] } order by {PRank desc}; Stock PRank Quote ---------- ----- ---------- IBM 6 171 IBM 5 155 IBM 4 171 IBM 3 125 IBM 2 157 IBM 1 107 <No Value> -6 <No Value> <No Value> -7 <No Value> <No Value> -8 <No Value> <No Value> -9 <No Value> <No Value> -10 <No Value> <No Value> -11 <No Value> The fact that the table has to be repopulated for each rank underscores the inefficiency of the subquery. There is no reuse of information from one rank to the next. When we compute the running sum for 'IBM': var S:=SQLQuery('select Stock,QTime,Quote, row_number()over(Partition by Stock Order by QTime,Quote) as PRank from Stocks') {Stock,PRank,Quote} ; select S add { Sum ( Quote from ( table { IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-11],row{-11 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-10],row{-10 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-9], row{ -9 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-8], row{ -8 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-7], row{ -7 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-6], row{ -6 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-5], row{ -5 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-4], row{ -4 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-3], row{ -3 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-2], row{ -2 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-1], row{ -1 PRank,nil Stock,nil Quote}), (S adorn{key{Stock,PRank}})[Stock,PRank] } ) ) SumQte } ; Stock PRank Quote SumQte -------- ----- ----- ------ IBM 1 107 107 IBM 2 157 264 IBM 3 125 389 IBM 4 171 560 IBM 5 155 715 IBM 6 171 886 IBM 7 219 1105 IBM 8 133 1238 IBM 9 139 1377 MS 1 257 257 . We are making 45 inserts into the table since we are starting over from the 1st rank for each row of 'IBM': 1+2+3+4+5+6+7+8+9=45 inserts. In the example operators D4ApplyforForm (used on the before insert event) and D4ApplyforFormUpdate (used on the before update event) represent the the idea of having to build a solution independently for each row. It therefore represents an inefficient solution just as a subquery does. Obviously it would make a lot more sense to simply add each new row to the table which had all the previous rows and then get the sum. We could represent the addition of a single row to the table as follows: //Shows reusing information from one PRank to another. var S:=SQLQuery('select Stock,QTime,Quote, row_number()over(Partition by Stock Order by QTime,Quote) as PRank from Stocks') {Stock,PRank,Quote} ; var Stock:='IBM'; var PRank:=6; var T1:= table { IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-11],row{-11 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-10],row{-10 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-9], row{ -9 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-8], row{ -8 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-7], row{ -7 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-6], row{ -6 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-5], row{ -5 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-4], row{ -4 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-3], row{ -3 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-2], row{ -2 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-1], row{ -1 PRank,nil Stock,nil Quote}), (S adorn{key{Stock,PRank}})[Stock,PRank] } ; select T1 union table { IfNil((S adorn{key{Stock,PRank}})[Stock,PRank+1],row{-99 PRank,nil Stock,nil Quote}) } where IsNotNil(Quote) order by {PRank desc}; Stock PRank Quote ----- ----- ----- IBM 7 219 IBM 6 171 IBM 5 155 IBM 4 171 IBM 3 125 IBM 2 157 IBM 1 107 In this way there would be as many inserts into the table as rows for the Stock. In other words we would be scanning the data just once for the nine rows vs. the multiple scans for the subquery (table). We can show the idea of accumulating sums incrementally. For example the running sum for row 6, adding the next row to the previously stored table and then getting the sum for row 7: var S:=SQLQuery('select Stock,QTime,Quote, row_number()over(Partition by Stock Order by QTime,Quote) as PRank from Stocks') {Stock,PRank,Quote} ; var Stock:='IBM'; var PRank:=6; var T1:= table { IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-11],row{-11 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-10],row{-10 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-9], row{ -9 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-8], row{ -8 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-7], row{ -7 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-6], row{ -6 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-5], row{ -5 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-4], row{ -4 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-3], row{ -3 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-2], row{ -2 PRank,nil Stock,nil Quote}), IfNil((S adorn{key{Stock,PRank}})[Stock,PRank-1], row{ -1 PRank,nil Stock,nil Quote}), (S adorn{key{Stock,PRank}})[Stock,PRank] } ; select table { row{'Accumulation of Sum for row 6' Desc,Sum(Quote from T1) SumQte}, row{'Accumulation of Sum for row 7', Sum(Quote from ( T1 union table { IfNil((S adorn{key{Stock,PRank}})[Stock,PRank+1],row{-99 PRank,nil Stock,nil Quote}) } where IsNotNil(Quote) )) } }; Desc SumQte ----------------------------- ------ Accumulation of Sum for row 6 886 Accumulation of Sum for row 7 1105 In the example operators D4WindowforForm (used on the before insert event) and D4WindowforFormUpdate (used on the before update event) represent the the idea of reusing prior information when appropriate by incrementally adding (and when appropriate deleting) rows from a work table that is used to obtain cumulative aggregates. View operator D4WindowforForm here. The Sql window The idea of accumulating running sums based on a new construct was introduced in the sql-99 standard: ISO/ANSI: Introduction to OLAP functions http://tinyurl.com/2taahc 'An <OLAP function> is defined using a window. A window may specify a partitioning, an ordering of rows within partitions, and an aggregation group. The aggregation group specifies which rows of a partition, relative to the current row, should participate in the calculation of an aggregate. Through aggregation groups, windows support such important OLAP capabilities as cumulative sums and moving averages. Windows may be specified either in the new WINDOW clause, or in-line in the SELECT list.' The running sums for Stock is expressed by: SELECT Stock,QTime,Quote, Sum(Quote) OVER (PARTITION BY Stock ORDER BY QTime,Quote 'ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW') AS SumQte FROM Stocks; where the window is: Sum(Quote) OVER (PARTITION BY Stock ORDER BY QTime,Quote 'ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW') AS SumQte The 'ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW' is called the aggregate group clause/statement. The definition of the terms is as follows: window-aggregation-group-clause The aggregation group of a row R is a set of rows, defined relative to R in the ordering of the rows of R's partition. This clause specifies the aggregation group. ROWS Indicates the aggregation group is defined by counting rows. group-BETWEEN Specifies the aggregation group start and end based on ROWS. UNBOUNDED PRECEDING Includes the entire partition preceding the current row. UNBOUNDED FOLLOWING Includes the entire partition following the current row. CURRENT ROW Specifies the start or end of the aggregation group as the current row. PRECEDING Specifies the number of rows preceding the current row.as a positive integer indicating a number of rows. FOLLOWING Specifies the number of rows following the current row. as a positive integer indicating a number of rows. The definition 'ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW' or its abbreviated form 'ROWS UNBOUNDED PRECEDING' means include all the rows in the partition prior to and including the current row in the computation of the sum. The sql-99 window does not introduce anything that couldn't be done previously. What it does is simplify the way it is done and most importantly make accumulation much more efficient. We can express the idea of the sql window in Sql Server with the following procedure: create procedure SqlWindow @From INT, @To INT as SELECT A.PRank,A.Stock,A.QTime,A.Quote, WCnt,SumQte,MinQte,MaxQte,AvgQte FROM (SELECT Stock,QTime,Quote, ROW_NUMBER() OVER(PARTITION BY Stock ORDER BY QTime) AS PRank FROM Stocks) AS A CROSS APPLY ( SELECT COUNT(*) AS WCnt,SUM(Quote) AS SumQte,MIN(Quote) AS MinQte, MAX(Quote) AS MaxQte,CAST(AVG(1.*Quote) AS DECIMAL(6,1)) AS AvgQte FROM (SELECT Stock,Quote,ROW_NUMBER() OVER(ORDER BY QTime) AS PRank FROM Stocks AS B WHERE B.Stock=A.Stock) AS C WHERE C.PRank BETWEEN A.PRank+@From AND A.PRank+@To ) AS D The @From and @To parameters are the bound of rows relative to the current row that the running/cumulative sum is obtained for. To obtain the running sums (and other aggregates) we can execute the procedure using an aggregate group definition that includes all prior rows in the sum: select SQLQuery ( 'Exec SqlWindow @From,@To', WList('ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW') {Wfrom From,Wto To} ); PRank Stock QTime Quote WCnt SumQte MinQte MaxQte AvgQte ----- -------- ---------------------- ----- ---- ------ ------ ------ ------ 1 IBM 4/3/2006 2:47:00 PM 107 1 107 107 107 107.0 2 IBM 7/16/2006 7:39:00 AM 157 2 264 107 157 132.0 3 IBM 8/26/2006 6:24:00 PM 125 3 389 107 157 129.7 4 IBM 9/8/2006 1:11:00 PM 171 4 560 107 171 140.0 5 IBM 11/22/2006 12:01:00 PM 155 5 715 107 171 143.0 6 IBM 2/2/2007 12:03:00 AM 171 6 886 107 171 147.7 7 IBM 4/13/2007 3:30:00 AM 219 7 1105 107 219 157.9 8 IBM 10/18/2007 12:12:00 AM 133 8 1238 107 219 154.8 9 IBM 12/25/2007 10:48:00 AM 139 9 1377 107 219 153.0 1 MS 3/17/2006 7:12:00 PM 257 1 257 257 257 257.0 . . The D4 operator WList parses the aggregate definition to obtain the @From and @To values. Executing the WList operator with the definition we obtain the bounds of the cumulative sum. WList returns a row with the From and To values: select WList('ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW'); Wfrom Wto ----------- --- -1000000000 0 The Wfrom value of -1000000000 simply represents a large negative number guaranteed to capture all prior rows. The value of 0 for Wto represents the current row. The specify construct '{Wfrom From,Wto To}' simply renames the columns of the row returned by operator WList to the names of the parameters expected by the sql procedure SqlWindow. If we wanted cumulative sums from the current row thru all rows available for a Stock we can use either of the following: select WList('ROWS UNBOUNDED FOLLOWING'); select WList('ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING'); Wfrom Wto ----- ---------- 0 1000000000 The Wfrom value of 0 is the current row and the Wto value of 1000000000 guarantees we will include all available 'following' rows in the partition for the cumulative sum. Executing the SqlWindow procedure: select SQLQuery ( 'Exec SqlWindow @From,@To', WList('ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING') {Wfrom From,Wto To} ); PRank Stock QTime Quote WCnt SumQte MinQte MaxQte AvgQte ----- -------- ---------------------- ----- ---- ------ ------ ------ ------ 1 IBM 4/3/2006 2:47:00 PM 107 9 1377 107 219 153.0 2 IBM 7/16/2006 7:39:00 AM 157 8 1270 125 219 158.8 3 IBM 8/26/2006 6:24:00 PM 125 7 1113 125 219 159.0 4 IBM 9/8/2006 1:11:00 PM 171 6 988 133 219 164.7 5 IBM 11/22/2006 12:01:00 PM 155 5 817 133 219 163.4 6 IBM 2/2/2007 12:03:00 AM 171 4 662 133 219 165.5 7 IBM 4/13/2007 3:30:00 AM 219 3 491 133 219 163.7 8 IBM 10/18/2007 12:12:00 AM 133 2 272 133 139 136.0 9 IBM 12/25/2007 10:48:00 AM 139 1 139 139 139 139.0 <- Last row 1 MS 3/17/2006 7:12:00 PM 257 11 2254 100 290 204.9 . . You can see that it is the opposite of 'ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW'. For the last row in the partition we are left with only the current row being used for the cumulative sum. Suppose we want sums for the current row, 2 prior rows and 1 following row. The aggregate group definition would be: select WList('ROWS BETWEEN 2 PRECEDING AND 1 FOLLOWING'); Wfrom Wto ----- --- -2 1 This definition therefore can use 4 rows, the 2 prior rows from the current row, the current row and the next row. select SQLQuery ( 'Exec SqlWindow @From,@To', WList('ROWS BETWEEN 2 PRECEDING AND 1 FOLLOWING') {Wfrom From,Wto To} ); PRank Stock QTime Quote WCnt SumQte MinQte MaxQte AvgQte ----- -------- ---------------------- ----- ---- ------ ------ ------ ------ 1 IBM 4/3/2006 2:47:00 PM 107 2 264 107 157 132.0 <- First row 2 IBM 7/16/2006 7:39:00 AM 157 3 389 107 157 129.7 3 IBM 8/26/2006 6:24:00 PM 125 4 560 107 171 140.0 4 IBM 9/8/2006 1:11:00 PM 171 4 608 125 171 152.0 5 IBM 11/22/2006 12:01:00 PM 155 4 622 125 171 155.5 6 IBM 2/2/2007 12:03:00 AM 171 4 716 155 219 179.0 7 IBM 4/13/2007 3:30:00 AM 219 4 678 133 219 169.5 8 IBM 10/18/2007 12:12:00 AM 133 4 662 133 219 165.5 9 IBM 12/25/2007 10:48:00 AM 139 3 491 133 219 163.7 1 MS 3/17/2006 7:12:00 PM 257 2 424 167 257 212.0 . . Note that not all rows have 4 values. For the 1st row (PRank=1) there are no prior rows, there is only the current row and the one following. Therefore the aggregates for the 1st row are based on cumulative values from only 2 rows. Note that a cumulative sum need not include the current row. We can base the accumulation on prior or following rows. For example we can use the prior rows between 4 and 2: select WList('ROWS BETWEEN 4 PRECEDING AND 2 PRECEDING') Wfrom Wto ----- --- -4 -2 select SQLQuery ( 'Exec SqlWindow @From,@To', WList('ROWS BETWEEN 4 PRECEDING AND 2 PRECEDING') {Wfrom From,Wto To} ); PRank Stock QTime Quote WCnt SumQte MinQte MaxQte AvgQte ----- -------- ---------------------- ----- ---- ---------- ---------- ---------- ---------- 1 IBM 4/3/2006 2:47:00 PM 107 0 <No Value> <No Value> <No Value> <No Value> 2 IBM 7/16/2006 7:39:00 AM 157 0 <No Value> <No Value> <No Value> <No Value> 3 IBM 8/26/2006 6:24:00 PM 125 1 107 107 107 107.0 <-First value 4 IBM 9/8/2006 1:11:00 PM 171 2 264 107 157 132.0 5 IBM 11/22/2006 12:01:00 PM 155 3 389 107 157 129.7 6 IBM 2/2/2007 12:03:00 AM 171 3 453 125 171 151.0 7 IBM 4/13/2007 3:30:00 AM 219 3 451 125 171 150.3 8 IBM 10/18/2007 12:12:00 AM 133 3 497 155 171 165.7 9 IBM 12/25/2007 10:48:00 AM 139 3 545 155 219 181.7 1 MS 3/17/2006 7:12:00 PM 257 0 <No Value> <No Value> <No Value> <No Value> 2 MS 5/9/2006 3:12:00 PM 167 0 <No Value> <No Value> <No Value> <No Value> 3 MS 6/24/2006 1:51:00 AM 277 1 257 257 257 257.0 . . It's not until the 3rd row of a partition that a row in included. We can lead or lag by using the same value from both From and To. For example to lead by 1 row we can use: select SQLQuery ( 'Exec SqlWindow @From,@To', WList('ROWS BETWEEN 1 FOLLOWING AND 1 FOLLOWING') {Wfrom From,Wto To} ); PRank Stock QTime Quote WCnt SumQte MinQte MaxQte AvgQte ----- -------- ---------------------- ----- ---- ---------- ---------- ---------- ---------- 1 IBM 4/3/2006 2:47:00 PM 107 1 157 157 157 157.0 2 IBM 7/16/2006 7:39:00 AM 157 1 125 125 125 125.0 3 IBM 8/26/2006 6:24:00 PM 125 1 171 171 171 171.0 4 IBM 9/8/2006 1:11:00 PM 171 1 155 155 155 155.0 5 IBM 11/22/2006 12:01:00 PM 155 1 171 171 171 171.0 6 IBM 2/2/2007 12:03:00 AM 171 1 219 219 219 219.0 7 IBM 4/13/2007 3:30:00 AM 219 1 133 133 133 133.0 8 IBM 10/18/2007 12:12:00 AM 133 1 139 139 139 139.0 9 IBM 12/25/2007 10:48:00 AM 139 0 <No Value> <No Value> <No Value> <No Value> <-Last row 1 MS 3/17/2006 7:12:00 PM 257 1 167 167 167 167.0 2 MS 5/9/2006 3:12:00 PM 167 1 277 277 277 277.0 3 MS 6/24/2006 1:51:00 AM 277 1 250 250 250 250.0 4 MS 8/18/2006 6:46:00 PM 250 1 290 290 290 290.0 5 MS 1/16/2007 5:56:00 PM 290 1 177 177 177 177.0 6 MS 1/22/2007 1:28:00 AM 177 1 150 150 150 150.0 7 MS 5/30/2007 4:30:00 PM 150 1 263 263 263 263.0 8 MS 6/21/2007 1:54:00 PM 263 1 153 153 153 153.0 9 MS 7/17/2007 12:20:00 PM 153 1 170 170 170 170.0 10 MS 11/26/2007 1:45:00 AM 170 1 100 100 100 100.0 11 MS 12/25/2007 3:54:00 PM 100 0 <No Value> <No Value> <No Value> <No Value> <-Last row 1 MySQL_AB 2/8/2006 3:47:00 PM 21 1 25 25 25 25.0 . . The last row for each partition (Stock) has no data in the accumulation. Note that the lead precludes the use of previous information. Modeling the Sql Window The D4 operators that compute cumulative aggregates, (D4ApplyforForm, D4ApplyforFormUpdate, D4WindowforForm and D4WindowforFormUpdate) take the sql procedure SqlWindow as their logical model. The operators implement the logic of the procedure using cursors as a way to illustrate their use. As stated previously the D4Apply operators follow the sql procedure more faithfully and reflect the same inefficiency as the procedure. The D4Window operators follow the same basic logic but make use of previous rows. This interdependency reflects a much more efficient methodology and hence is more faithful to the idea of the efficient sql window.
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)
Friday, July 13, 2007
Dataphor - An example of rapid application development
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment