I have 3 tables in Table 2 we have columns with columnName Field they can grow Dynamically at that time we have just 5 columns for Each CTypeId they can be 6 or 10 etc. In Table3 we have the column values.
For example AccountManager From Table 2 have value in Table 3 Jack / Kate
similarly other columns and their values are
ColumnName | Values
Channel | PS
StartDate | 06/03/2017
I want Result Like this
I have tried using Pivot Function with the following query:
Declare #Columns nvarchar(max)
Declare #a nvarchar(max)
Set #Columns = (select STUFF((select ',' + '[' + Convert(varchar(200), ColumnName) + ']' from CharityTypeInformationDynamicFields FOR XML PATH('')), 1,1, ''))
Declare #sql nvarchar(max)
= 'Select *
from
(select cd.Id, cd.Value, ci.ColumnName
from Table3 cd
Inner Join Table2 ci
on ci.Id = cd.DynamicFieldID
) as s
Pivot(MAX(Value) ForColumnName IN ('+#columns+')) as pvt'
Select #sql
But the query gives the result:
What do I need to change to achieve my desired output?
There are a few issues that you need to solve in order to get the result you desire. But before trying a dynamic sql version of a query I'd always recommend that you try get your final result by writing a hard-coded or static version first. This allows you to get the desired result without bugs and then convert it to dynamic sql as your final query.
First, let's get your table structures and sample data into a reusable script. It appears that you only need table2 and table3 to get your end result:
create table #table2
(
id int,
ctypeid int,
columnname varchar(50)
)
insert into #table2
values
(1, 20, 'Account Manager'), (2, 20, 'Channel'),
(3, 20, 'Start Date'), (4, 20, 'End Date'),
(5, 20, 'Gross Annual'), (6, 6, 'Account Manager'),
(7, 6, 'Channel'), (8, 6, 'Start Date'),
(9, 6, 'End Date'), (10, 6, 'Gross Annual');
create table #table3
(
id int,
table2id int,
value varchar(50)
)
insert into #table3
values
(1, 1, 'Jack / Kate'), (2, 2, 'PS'), (3, 3, '06/03/2017'),
(4, 4, '07/03/2017'), (5, 5, '2500'), (6, 6, 'Ollie'),
(7, 7, 'D2D'), (8, 8, '06/03/2017'), (9, 9, '06/03/2017'),
(10, 10, '5232'), (11, 1, 'Jack'), (12, 2, 'PSP'),
(13, 3, '06/03/2017'), (14, 4, '07/03/2017'), (15, 5, '7000'),
(16, 1, 'Jack Sparrow'), (17, 2, 'PS Sparrow'), (1, 3, '06/03/2017'),
(19, 4, '07/03/2017'), (20, 5, '3000'), (21, 6, 'John'),
(22, 7, 'JEDF'), (23, 8, '06/03/2017'), (24, 9, '06/03/2017'),
(25, 10, '5232');
Next, you need to write your PIVOT query. Your final result only includes the values from 3 columns CTypeId, Value, and ColumnName, so the start of your query PIVOT would be:
select
CTypeId,
[Account Manager], [Channel], [Start Date],
[End Date], [Gross Annual]
from
(
select ci.CTypeId, cd.Value, ci.ColumnName
from #Table3 cd
Inner Join #Table2 ci
on ci.Id = cd.Table2Id
) d
pivot
(
max(Value)
for ColumnName in ([Account Manager], [Channel], [Start Date],
[End Date], [Gross Annual])
) piv
Demo. But since you're aggregating string values in the Value column, you will only return one row for each CTypeId:
+---------+-----------------+---------+------------+------------+---------------+
| CTypeId | Account Manager | Channel | Start Date | End Date | Gross Annual |
+---------+-----------------+---------+------------+------------+---------------+
| 6 | Ollie | JEDF | 06/03/2017 | 06/03/2017 | 5232 |
| 20 | Jack Sparrow | PSP | 06/03/2017 | 07/03/2017 | 7000 |
+---------+-----------------+---------+------------+------------+---------------+
which is not what you want, so you need to do something to allow for multiple rows. If you look at a sample of the data that is returned by the subquery:
+---------+-------------+------------------+
| CTypeId | Value | ColumnName |
+---------+-------------+------------------+
| 20 | Jack / Kate | Account Manager |
| 20 | PS | Channel |
| 20 | 06/03/2017 | Start Date |
| 20 | 07/03/2017 | End Date |
| 20 | 2500 | Gross Annual |
| 6 | Ollie | Account Manager |
| 6 | D2D | Channel |
| 6 | 06/03/2017 | Start Date |
| 6 | 06/03/2017 | End Date |
| 6 | 5232 | Gross Annual |
+---------+-------------+------------------+
You'll see that you have unique data over a combination of CTypeId and ColumnName values, so you can create a unique row number using the windowing function row_number in your subquery which can be used to uniquely group the data for a pivot. By changing the above PIVOT code to:
select
CTypeId,
[Account Manager], [Channel], [Start Date],
[End Date], [Gross Annual]
from
(
select ci.CTypeId, cd.Value, ci.ColumnName,
rn = row_number() over(partition by ci.CTypeId, ci.ColumnName order by cd.Value)
from #Table3 cd
Inner Join #Table2 ci
on ci.Id = cd.Table2Id
) d
pivot
(
max(Value)
for ColumnName in ([Account Manager], [Channel], [Start Date],
[End Date], [Gross Annual])
) piv
order by CTypeId
See demo, you get the desired result:
+---------+-----------------+------------+------------+------------+---------------+
| CTypeId | Account Manager | Channel | Start Date | End Date | Gross Annual |
+---------+-----------------+------------+------------+------------+---------------+
| 6 | John | D2D | 06/03/2017 | 06/03/2017 | 5232 |
| 6 | Ollie | JEDF | 06/03/2017 | 06/03/2017 | 5232 |
| 20 | Jack | PS | 06/03/2017 | 07/03/2017 | 2500 |
| 20 | Jack / Kate | PS Sparrow | 06/03/2017 | 07/03/2017 | 3000 |
| 20 | Jack Sparrow | PSP | 06/03/2017 | 07/03/2017 | 7000 |
+---------+-----------------+------------+------------+------------+---------------+
Once you've got your final result you want, it's easy to convert the query to dynamic SQL:
Declare #Columns nvarchar(max)
Declare #a nvarchar(max)
Set #Columns = stuff((select distinct ',' + quotename(ColumnName)
from #table2
for xml path(''), type).value('.', 'nvarchar(max)'), 1, 1, '');
Declare #sql nvarchar(max)
= 'Select CTypeId, '+#Columns+'
from
(
select ci.CTypeId, cd.Value, ci.ColumnName,
rn = row_number() over(partition by ci.CTypeId, ci.ColumnName order by cd.Value)
from #Table3 cd
Inner Join #Table2 ci
on ci.Id = cd.Table2Id
) as s
Pivot(MAX(Value) For ColumnName IN ('+#columns+')) as pvt
order by CTypeId'
execute(#sql);
See Demo. This gives the same result as the hard-coded version with the flexibility of dynamic sql.
Related
I'm currently trying to track the changes of a few columns (let's call them col1 & col2) in a SQL Server table. The table is not being "updated/inserted/deleted" over time; new records are just being added to it (please see below 10/01 vs 11/01).
My end-goal would be to run a SQL query or stored procedure that would highlight the changes overtime using primary keys following the framework:
PrimaryKey | ColumnName | BeforeValue | AfterValue | Date
e.g.
Original table:
+-------+--------+--------+--------+
| PK1 | Col1 | Col2 | Date |
+-------+--------+--------+--------+
| 1 | a | e | 10/01 |
| 1 | b | e | 11/01 |
| 2 | c | e | 10/01 |
| 2 | d | f | 11/01 |
+-------+--------+--------+--------+
Output:
+--------------+--------------+---------------+--------------+--------+
| PrimaryKey | ColumnName | BeforeValue | AfterValue | Date |
+--------------+--------------+---------------+--------------+--------+
| 1 | Col1 | a | b | 11/01 |
| 2 | Col1 | c | d | 11/01 |
| 2 | Col2 | e | f | 11/01 |
+--------------+--------------+---------------+--------------+--------+
Any help appreciated.
Here is some code which is a bit clunky, but seems to work, Basically for each row I try and find an earlier row with a different value. This is done twice, once for Col1 and once for Col2.
To make it work I had to add a unique PK field, which I don't know whether you have or not, you can easily add as an identify field, either to your real table, or to the table used for the calculations.
declare #TestTable table (PK int, PK1 int, Col1 varchar(1), Col2 varchar(1), [Date] date)
insert into #TestTable (PK, PK1, Col1, Col2, [Date])
select 1, 1, 'a', 'e', '10 Jan 2018'
union all select 2, 1, 'b', 'e', '11 Jan 2018'
union all select 3, 2, 'c', 'e', '10 Jan 2018'
union all select 4, 2, 'd', 'f', '11 Jan 2018'
select T1.[Date], T1.PK1, 'Col1', T2.Col1, T1.Col1
from #TestTable T1
inner join #TestTable T2 on T2.PK = (
select top 1 PK
from #TestTable T21
where T21.PK1 = T1.PK1 and T21.Col1 != T1.Col1 and T21.[Date] < T1.[Date]
order by T21.[Date] desc
)
union all
select T1.[Date], T1.PK1, 'Col2', T3.Col2, T1.Col2
from #TestTable T1
inner join #TestTable T3 on T3.PK = (
select top 1 PK
from #TestTable T31
where T31.PK1 = T1.PK1 and T31.Col2 != T1.Col2 and T31.[Date] < T1.[Date]
order by T31.[Date] desc
)
order by [Date], PK1
The audit table looks like this:
Audit ID VendorID PaymentType CreateDateUTC
999 8048 2 2017-10-30-08:84:24
1000 1234 5 2017-10-31-01:17:34
1001 8048 7 2017-10-31-01:17:45
1002 1234 5 2017-10-31-01:17:53
1003 1234 7 2017-10-31-01:18:23
1004 1234 5 2017-11-01-01:18:45
In this example, you can see that say - VendorID 1234 started as PaymentType 5, then had another entry where it's still 5 (the audit table records additional changes not relevant to my query), then it changes to 7, but then back to 5.
Say I'd want to answer the question: 'Between now and date X, these VendorIDs had a change in PaymentType'. A bonus would be - this was the previous PaymentType.
Expected Results:
VendorID PaymentType Prev_PaymentType
8048 7 2
So say if I queried between now and 10-31-01:00:00, I'd want it to return VendorID 8048 as having changed (and as a bonus, that it's previous PaymentType was 2), but VendorID 1234 shouldn't show up, since at 2017-10-31-01:00:00 it was a 5, and now is still a 5, despite the intermittent changes.
How would one go about querying the VendorIDs whose payment type changed between 2 dates?
Thanks!
Here is an alternative approach that my prove useful, using OUTER APPLY. Note that the AuditID column is used as a tie-breaker mostly because the sample data does not have datetime values.
SQL Fiddle
CREATE TABLE AuditTable (
AuditID int
, VendorID int
, PaymentType int
, CreateDateUTC date
);
INSERT INTO AuditTable
VALUES (999, 8048, 2, '2017-10-30'),
(1000, 1234, 5, '2017-10-31'),
(1001, 8048, 7, '2017-10-31'),
(1002, 1234, 5, '2017-10-31'),
(1003, 1234, 7, '2017-10-31'),
(1004, 1234, 5, '2017-11-01');
Query 1:
select
*
from AuditTable a
outer apply (
select top(1) PaymentType, CreateDateUTC
from AuditTable t
where a.VendorID = t.VendorID
and a.CreateDateUTC >= t.CreateDateUTC
and a.AuditID > t.AuditID
order by CreateDateUTC DESC, AuditID DESC
) oa (PrevPaymentType, PrevDate)
order by
vendorid
, CreateDateUTC
Results:
| AuditID | VendorID | PaymentType | CreateDateUTC | PrevPaymentType | PrevDate |
|---------|----------|-------------|---------------|-----------------|------------|
| 1000 | 1234 | 5 | 2017-10-31 | (null) | (null) |
| 1002 | 1234 | 5 | 2017-10-31 | 5 | 2017-10-31 |
| 1003 | 1234 | 7 | 2017-10-31 | 5 | 2017-10-31 |
| 1004 | 1234 | 5 | 2017-11-01 | 7 | 2017-10-31 |
| 999 | 8048 | 2 | 2017-10-30 | (null) | (null) |
| 1001 | 8048 | 7 | 2017-10-31 | 2 | 2017-10-30 |
CREATE TABLE AuditTable (
AuditID INT,
VendorID INT,
PaymentType INT,
CreateDateUTC DATE
);
INSERT INTO AuditTable VALUES
(999 , 8048, 2, '2017-10-30'),
(1000, 1234, 5, '2017-10-31'),
(1001, 8048, 7, '2017-10-31'),
(1002, 1234, 5, '2017-10-31'),
(1003, 1234, 7, '2017-10-31'),
(1004, 1234, 5, '2017-11-01');
WITH CTE AS (
SELECT *,
ROW_NUMBER () OVER (PARTITION BY CreateDateUTC ORDER BY PaymentType) AS N1
FROM AuditTable
WHERE CreateDateUTC <= '2017-11-02' AND CreateDateUTC >= '2017-10-01'
) ,
MAXP AS(
SELECT VendorID, PaymentType, CreateDateUTC
FROM CTE
WHERE N1 = (SELECT MAX(N1) FROM CTE)
)
SELECT TOP 1 MAXP.VendorID, MAXP.PaymentType AS PaymentType, CTE.PaymentType AS Prev_PaymentType
FROM MAXP
JOIN CTE ON CTE.VendorID = MAXP.VendorID;
Result:
+----------+-------------+------------------+
| VendorID | PaymentType | Prev_PaymentType |
+----------+-------------+------------------+
| 8048 | 7 | 2 |
+----------+-------------+------------------+
Demo
Here is a variant without using LEAD() or LAG() but does use ROW_NUMBER and COUNT() OVER().
See this verision work at:SQL Fiddle
CREATE TABLE AuditTable (
AuditID int
, VendorID int
, PaymentType int
, CreateDateUTC date
);
INSERT INTO AuditTable
VALUES (999, 8048, 2, '2017-10-30'),
(1000, 1234, 5, '2017-10-31'),
(1001, 8048, 7, '2017-10-31'),
(1002, 1234, 5, '2017-10-31'),
(1003, 1234, 7, '2017-10-31'),
(1004, 1234, 5, '2017-11-01');
Query 1:
WITH
rowz AS (
SELECT *
, ROW_NUMBER() OVER (PARTITION BY VendorID
ORDER BY CreateDateUTC, AuditID) AS lagno
FROM AuditTable
),
cte AS (
SELECT *
, ROW_NUMBER() OVER (PARTITION BY VendorID, CreateDateUTC
ORDER BY c DESC, span_dt) rn
FROM (
SELECT r1.AuditID, r1.VendorID, r1.CreateDateUTC
, r1.PaymentType AS prevpaymenttype
, r2.PaymentType
, COALESCE(r2.CreateDateUTC, CAST(GETDATE() AS date)) span_dt
, COUNT(*) OVER (PARTITION BY r1.VendorID, r1.CreateDateUTC, r1.PaymentType) c
FROM rowz r1
LEFT JOIN rowz r2 ON r1.VendorID = r2.VendorID
AND r1.lagno = r2.lagno - 1
) d
)
SELECT
AuditID, VendorID, PrevPaymentType, PaymentType, CreateDateUTC
FROM (
SELECT
*
FROM cte
WHERE ('20171031' BETWEEN CreateDateUTC AND span_dt AND rn = 1)
OR (CAST(GETDATE() AS date) BETWEEN CreateDateUTC AND span_dt AND rn = 1)
) d
WHERE PaymentType <> PrevPaymentType
Results:
| AuditID | VendorID | PrevPaymentType | PaymentType | CreateDateUTC |
|---------|----------|-----------------|-------------|---------------|
| 999 | 8048 | 2 | 7 | 2017-10-30 |
I am trying to create a pivot table in SQL 2008R2. I'm trying to reproduce and Access Pivot table in SQL. When I run the following script, I get one record for each pivot column instead of one record with two populated pivoted columns.
SELECT * FROM
(SELECT DataView.MAEID
, DataView.MoYr
, DataView.ChemicalName
, TblCategories.CatDesc
, DataView.[CAS#]
, DataView.HAP
, Sum([SpecAE]/2000) AS [Total SpecAE Tons]
, SUM([SpecAE]) AS [SpecAE]
FROM TblCategories
INNER JOIN DataView
ON TblCategories.CatID = DataView.Category
GROUP BY DataView.MAEID
, DataView.MoYr
, DataView.ChemicalName
, DataView.[CAS#]
, DataView.HAP
, TblCategories.CatDesc) TBL
PIVOT (
Sum([SpecAE])
FOR CatDesc IN ([INCIINERABLE LIQUIDS], [Supplemental Fuels])
)pvt
Any thoughts?
You haven't provided sample data, so I'll explain your issue with an example. Let's say I have a very simple table with some very simple values:
DECLARE #T TABLE
(
MainColumn INT NOT NULL,
PivotColumn INT NOT NULL,
SumColumn INT NOT NULL
);
INSERT #T VALUES
(1, 1, 10), (1, 1, 20), (1, 1, 30),
(1, 2, 60),
(1, 3, 50),
(2, 1, 10), (2, 1, 15),
(2, 2, 20),
(3, 1, 10),
(3, 2, 10),
(4, 1, 150);
If I perform the following query:
SELECT MainColumn,
PivotColumn,
PivotValue = SUM(SumColumn),
OtherSum = SUM(SumColumn / 5)
FROM #T
GROUP BY MainColumn, PivotColumn
ORDER BY MainColumn, PivotColumn
I get:
+------------+-------------+------------+----------+
| MainColumn | PivotColumn | PivotValue | OtherSum |
+------------+-------------+------------+----------+
| 1 | 1 | 60 | 12 |
| 1 | 2 | 60 | 12 |
| 1 | 3 | 50 | 10 |
| 2 | 1 | 25 | 5 |
| 2 | 2 | 20 | 4 |
| 3 | 1 | 10 | 2 |
| 3 | 2 | 10 | 2 |
| 4 | 1 | 150 | 30 |
+------------+-------------+------------+----------+
Now if I use a PIVOT to pivot the PivotValue for each PivotColumn, it's going to group by MainColumn AND OtherSum column. A pivot groups by every column that isn't part of the pivot.
So my result set will be split into (MainColumn=1, OtherSum=12), (MainColumn=1, OtherSum=10), (MainColumn=2, OtherSum=5), (MainColumn=2, OtherSum=4), etc... I will get a new line for each of these values. If the OtherSum value was unique for each line, I'd expect 8 lines with a pivot.
If I remove OtherSum from my result set, my result set is just going to group by MainColumn alone, so it'll all be on one line for each distinct MainColumn value, since that's the only column the pivot would group by.
If getting the other sum value is important, I can do something like the following:
SELECT P.MainColumn,
Val1A = P.[1],
Val1B = P.[1] / 5,
Val2A = P.[2],
Val2B = P.[2] / 5,
Val3A = P.[3],
Val3B = P.[3] / 5
FROM
(
SELECT MainColumn,
PivotColumn,
PivotValue = SUM(SumColumn)
FROM #T
GROUP BY MainColumn, PivotColumn
) AS T
PIVOT
(
SUM(PivotValue) FOR PivotColumn IN ([1], [2], [3])
) AS P;
Hi I have doubt in sql server
Table : emp
Id | Desc
1 | abc
2 | def
3 | har
table2 : emp1
Id | Desc
3 | Har
4 | jai
4 | jai
5 | uou
6 | uni
6 | udkey
2 | Jainiu
based on above table I want output like below
ID | Desc
1 | abc
2 | def
3 | har
4 | jai
5 | uou
6 | uni
I tried like below
select id, desc from emp
union
select * from (select *,row_number()over(partition by id)as rn from emp1)
where rn=1
after executing this query I got an error like below
Msg 205, Level 16, State 1, Line 2
All queries combined using a UNION, INTERSECT or EXCEPT operator must have an equal number of expressions in their target lists.
It's saying above 1st query are 2 column and 2 query are 3 column, this process
how we avoid this rn column.
please tell me how to write query to achive this task in sql server
Use:
SELECT id,
desc
FROM emp
UNION
SELECT id,
desc
FROM emp1
Union will automatically do a distinct and sort it in the proper order, you don't need to do anything, no windwos functions required
Result:
ID | Desc
1 | abc
2 | def
2 | Jainiu
3 | har
4 | jai
5 | uou
6 | uni
6 | udkey
If you want to remove udkey and Jainiu please mention a logi to choose between def and Jainiu and so on...
using the Union or Distinct we can reomve duplicates and row_number we can get output and In union should have equal number of columns
DECLARE #emp TABLE
([Id] int, [Desc] varchar(3))
;
INSERT INTO #emp
([Id], [Desc])
VALUES
(1, 'abc'),
(2, 'def'),
(3, 'har')
DECLARE #emp1 TABLE
([Id] int, [Desc] varchar(6))
INSERT INTO #emp1
([Id], [Desc])
VALUES
(3, 'Har'),
(4, 'jai'),
(4, 'jai'),
(5, 'uou'),
(6, 'uni'),
(6, 'udkey'),
(2, 'Jainiu')
;with CTE AS (
select
id,
[Desc],
Row_NUMBER()OVER(PARTITION BY ID ORDER BY ID,[Desc]DESC)RN
from
(select distinct id,[Desc] from #EMP
UNION ALL
select distinct id,[Desc] from #EMP1)T)
select id,[Desc] from CTE
WHERE RN = 1
I have this table and data
CREATE TABLE #transactions (
[transactionId] [int] NOT NULL,
[accountId] [int] NOT NULL,
[dt] [datetime] NOT NULL,
[balance] [smallmoney] NOT NULL,
CONSTRAINT [PK_transactions_1] PRIMARY KEY CLUSTERED
( [transactionId] ASC)
)
INSERT #transactions ([transactionId], [accountId], [dt], [balance]) VALUES
(1, 1, CAST(0x0000A13900107AC0 AS DateTime), 123.0000),
(2, 1, CAST(0x0000A13900107AC0 AS DateTime), 192.0000),
(3, 1, CAST(0x0000A13A00107AC0 AS DateTime), 178.0000),
(4, 2, CAST(0x0000A13B00107AC0 AS DateTime), 78.0000),
(5, 2, CAST(0x0000A13D011D1860 AS DateTime), 99.0000),
(6, 2, CAST(0x0000A13F00000000 AS DateTime), 97.0000),
(7, 1, CAST(0x0000A13D0141E640 AS DateTime), 201.0000),
(8, 3, CAST(0x0000A1420094DD60 AS DateTime), 4000.0000),
(9, 3, CAST(0x0000A14300956A00 AS DateTime), 4100.0000),
(10, 3, CAST(0x0000A14700000000 AS DateTime), 4200.0000),
(11, 2, CAST(0x0000A14B00B84BB0 AS DateTime), 110.0000)
I need two queries.
For each transaction, I want to return in a query the most recent balance for each account, and an extra column with a SUM of each account balance at that point in time.
Same as 1 but grouped by date without the time portion. So the latest account balance at the end of each day (where there is a transaction in any account) for each account, but SUMed together as in 1.
Data above is sample data that I just made up, but my real table has hundreds of rows and ten accounts (which may increase soon). Each account has a unique accountId. Seems quite a tricky piece of SQL.
EXAMPLE
For 1. I need a result like this:
+---------------+-----------+-------------------------+---------+-------------+
| transactionId | accountId | dt | balance | sumBalances |
+---------------+-----------+-------------------------+---------+-------------+
| 1 | 1 | 2013-01-01 01:00:00.000 | 123 | 123 |
| 2 | 1 | 2013-01-01 01:00:00.000 | 192 | 192 |
| 3 | 1 | 2013-01-02 01:00:00.000 | 178 | 178 |
| 4 | 2 | 2013-01-03 01:00:00.000 | 78 | 256 |
| 5 | 2 | 2013-01-05 17:18:00.000 | 99 | 277 |
| 7 | 1 | 2013-01-05 19:32:00.000 | 201 | 300 |
| 6 | 2 | 2013-01-07 00:00:00.000 | 97 | 298 |
| 8 | 3 | 2013-01-10 09:02:00.000 | 4000 | 4298 |
| 9 | 3 | 2013-01-11 09:04:00.000 | 4100 | 4398 |
| 10 | 3 | 2013-01-15 00:00:00.000 | 4200 | 4498 |
| 11 | 2 | 2013-01-19 11:11:00.000 | 110 | 4511 |
+---------------+-----------+-------------------------+---------+-------------+
So, for transactionId 8, I take the latest balance for each account in turn and then sum them. AccountID 1: is 201, AccountId 2 is 97 and AccountId 3 is 4000. Therefore the result for transactionId 8 will be 201+97+4000 = 4298. When calculating the set must be ordered by dt
For 2. I need this
+------------+-------------+
| date | sumBalances |
+------------+-------------+
| 01/01/2013 | 192 |
| 02/01/2013 | 178 |
| 03/01/2013 | 256 |
| 05/01/2013 | 300 |
| 07/01/2013 | 298 |
| 10/01/2013 | 4298 |
| 11/01/2013 | 4398 |
| 15/01/2013 | 4498 |
| 19/01/2013 | 4511 |
+------------+-------------+
So on date 15/01/2013 the latest account balance for each account in turn (1,2,3) is 201,97,4200. So the result for that date would be 201+97+4200 = 4498
This gives your first desired resultset (SQL Fiddle)
WITH T
AS (SELECT *,
balance -
isnull(lag(balance) OVER (PARTITION BY accountId
ORDER BY dt, transactionId), 0) AS B
FROM #transactions)
SELECT transactionId,
accountId,
dt,
balance,
SUM(B) OVER (ORDER BY dt, transactionId ROWS UNBOUNDED PRECEDING) AS sumBalances
FROM T
ORDER BY dt;
It subtracts the current balance of the account from the previous balance to get the net difference then calculates a running total of those differences.
And that can be used as a base for your second result
WITH T1
AS (SELECT *,
balance -
isnull(lag(balance) OVER (PARTITION BY accountId
ORDER BY dt, transactionId), 0) AS B
FROM #transactions),
T2 AS (
SELECT transactionId,
accountId,
dt,
balance,
ROW_NUMBER() OVER (PARTITION BY CAST(dt AS DATE) ORDER BY dt DESC, transactionId DESC) AS RN,
SUM(B) OVER (ORDER BY dt, transactionId ROWS UNBOUNDED PRECEDING) AS sumBalances
FROM T1)
SELECT CAST(dt AS DATE) AS [date], sumBalances
FROM T2
WHERE RN=1
ORDER BY [date];
Part 1
; WITH a AS (
SELECT *, r = ROW_NUMBER()OVER(PARTITION BY accountId ORDER BY dt)
FROM #transactions t
)
, b AS (
SELECT t.*
, transamount = t.balance - ISNULL(t0.balance,0)
FROM a t
LEFT JOIN a t0 ON t0.accountId = t.accountId AND t0.r + 1 = t.r
)
SELECT transactionId, accountId, dt, balance
, sumBalance = SUM(transamount)OVER(ORDER BY dt, transactionId)
FROM b
ORDER BY dt
Part 2
; WITH a AS (
SELECT *, r = ROW_NUMBER()OVER(PARTITION BY accountId ORDER BY dt)
FROM #transactions t
)
, b AS (
SELECT t.*
, transamount = t.balance - ISNULL(t0.balance,0)
FROM a t
LEFT JOIN a t0 ON t0.accountId = t.accountId AND t0.r + 1 = t.r
)
, c AS (
SELECT transactionId, accountId, dt, balance
, sumBalance = SUM(transamount)OVER(ORDER BY CAST(dt AS DATE))
, r1 = ROW_NUMBER()OVER(PARTITION BY accountId, CAST(dt AS DATE) ORDER BY dt DESC)
FROM b
)
SELECT dt = CAST(dt AS DATE)
, sumBalance
FROM c
WHERE r1 = 1
ORDER BY CAST(dt AS DATE)