How to Group based on Distinct value of other column - sql-server

I have this input table
+--------+-----------+------------+----------+
| TaskId | OwnerName | WorkerName | Category |
+--------+-----------+------------+----------+
| 1 | Sara | Sara | 1 |
| 1 | Sara | Maya | 1 |
| 1 | Sara | Sara | 1 |
| 2 | Sara | Sara | 0 |
| 2 | Sara | Sara | 0 |
| 3 | Sam | Sam | 1 |
| 3 | Sam | Sam | 1 |
| 3 | Sam | Sam | 1 |
| 4 | Ella | Ella | 1 |
| 4 | Ella | Ella | 1 |
| 5 | Ella | Ella | 1 |
| 6 | Ella | Ella | 0 |
+--------+-----------+------------+----------+
I want to calculate how many times the owner name has in category column high (1) or low (0).
The tasks should be counted as Distinct , also if a task has owner name != worker name... the entire task will be dropped.
For example, TaskId 1 will be dropped.
Expected Output:
+-----------+------------------+-----------------+-------+------+
| OwnerName | #UniqueHighTasks | #UniqueLowTasks | %High | %Low |
+-----------+------------------+-----------------+-------+------+
| Sara | 0 | 1 | 0 | 100 |
| Sam | 1 | 0 | 100 | 0 |
| Ella | 2 | 1 | 66% | 33% |
+-----------+------------------+-----------------+-------+------+
My Attempt isn't taking into consideration that the distinct ids and that task 1 will be dropped, how to accomplish that?
Attempt:
SELECT
[OwnerName],
(100 * COALESCE(COUNT(CASE
WHEN [Category] = 1 THEN 1
END), 0) /
(COALESCE(COUNT(CASE
WHEN [Category] = 1 THEN 1
END), 0) + COALESCE(COUNT(CASE
WHEN [Category] = 0 THEN 1
END), 0))) AS %High,
(100 * COALESCE(COUNT(CASE
WHEN [Category] = 0 THEN 1
END), 0) /
(COALESCE(COUNT(CASE
WHEN [Category] = 0 THEN 1
END), 0) + COALESCE(COUNT(CASE
WHEN [Category] = 1 THEN 1
END), 0))) AS %Low,
SUM(case [Category] when 1 then 1 else 0 end) AS '#UniqueHighTasks',
SUM(case [Category] when 0 then 1 else 0 end) AS '#UniqueLowTasks'
FROM [dbo].[mytable]
Group by [OwnerName]

Try this query:
declare #t table (TaskId int, OwnerName varchar(10), WorkerName varchar(10), Category int)
insert into #t
values
(1, 'Sara','Sara', 1), (1, 'Sara','Maya', 1)
, (1, 'Sara','Sara', 1), (2, 'Sara','Sara', 0)
, (2, 'Sara','Sara', 0), (3, 'Sam','Sam', 1)
, (3, 'Sam','Sam', 1), (3, 'Sam','Sam', 1)
, (4, 'Ella','Ella', 1), (4, 'Ella','Ella', 1)
, (5, 'Ella','Ella', 1), (6, 'Ella','Ella', 0)
select
OwnerName, UniqueHighTasks, UniqueLowTasks
, [%High] = UniqueHighTasks * 100.0 / (UniqueHighTasks + UniqueLowTasks)
, [%Low] = UniqueLowTasks * 100.0 / (UniqueHighTasks + UniqueLowTasks)
from (
select
OwnerName, UniqueHighTasks = count(distinct case when Category = 1 then TaskId end)
, UniqueLowTasks = count(distinct case when Category = 0 then TaskId end)
from
#t
where
TaskId not in (select TaskId from #t where OwnerName <> WorkerName)
group by OwnerName
) t

I think this will do it
declare #T table (TaskId int, OwnerName varchar(20), WorkerName varchar(20), Category int);
insert into #T values
(1, 'Sara', 'Sara', 1 )
, (1, 'Sara', 'Maya', 1 )
, (1, 'Sara', 'Sara', 1 )
, (2, 'Sara', 'Sara', 0 )
, (2, 'Sara', 'Sara', 0 )
, (3, 'Sam', 'Sam', 1 )
, (3, 'Sam', 'Sam', 1 )
, (3, 'Sam', 'Sam', 1 )
, (4, 'Ella', 'Ella', 1 )
, (4, 'Ella', 'Ella', 1 )
, (5, 'Ella', 'Ella', 1 )
, (6, 'Ella', 'Ella', 0 );
with cte as
( select distinct t.TaskId, t.OwnerName, t.Category
from #T t
where TaskID not in (select TaskId from #T where OwnerName <> WorkerName)
)
select cte.OwnerName
, count(*) taskCount
, sum(category) as highCount
, count(*) - sum(category) as lowCount
, 100.0*sum(category)/count(*) as highPct
, 100.0*(count(*)-sum(category))/count(*) as lowPct
from cte
group by cte.OwnerName
OwnerName taskCount highCount lowCount lowPct highPct
-------------------- ----------- ----------- ----------- --------------------------------------- ---------------------------------------
Ella 3 2 1 66.666666666666 33.333333333333
Sam 1 1 0 100.000000000000 0.000000000000
Sara 1 0 1 0.000000000000 100.000000000000

Take your current query, and instead of querying the table directly, query a CTE or derived table that simply does a SELECT DISTINCT from the table.

You could try this
WITH nonDup AS (
SELECT DISTINCT [TaskId], [OwnerName], [WorkerName], [Category]
FROM mytable t1
WHERE NOT EXISTS(SELECT 1 FROM mytable t2 WHERE t1.TaskID = t2.TaskID and t2.OwnerName <> t2.WorkerName)
),
calc AS (
SELECT
OwnerName,
SUM(Category) AS UniqueHighTasks,
SUM(CASE WHEN Category = 0 THEN 1 ELSE 0 END) AS UniqueLowTasks
FROM nonDup
GROUP BY OwnerName
)
SELECT OwnerName,UniqueHighTasks,UniqueLowTasks,
CASE WHEN UniqueHighTasks+ UniqueLowTasks<> 0 THEN UniqueHighTasks*1.0/(UniqueHighTasks+ UniqueLowTasks) END AS [%High],
CASE WHEN UniqueHighTasks+ UniqueLowTasks<> 0 THEN UniqueLowTasks*1.0/(UniqueHighTasks+ UniqueLowTasks) END AS [%Low]
FROM calc

Related

SQL Server stored procedure - how to sort the records with tree format?

I have a table with these records:
+----+-------------+---------+
| ID | Name | ParentID|
+----+-------------+---------+
| 1 | Item 1 | -1 |
| 2 | Item 2 | -1 |
| 3 | Item 1.1 | 1 |
| 4 | Item 1.2 | 1 |
| 5 | Item 2.1 | 2 |
| 6 | Item 1.1.1 | 3 |
| 7 | Item 1.2.1 | 4 |
| 8 | Item 2.2 | 2 |
| 9 | Item 1.1.1.1| 6 |
+----+-------------+---------+
I want select the records with tree format.
How to get result like below table with a stored procedure? The values of Name column are temporary, it could be ANY WORD.
+----+-------------+---------+
| ID | Name | ParentID|
+----+-------------+---------+
| 1 | Item 1 | -1 |
| 3 | Item 1.1 | 1 |
| 6 | Item 1.1.1 | 3 |
| 9 | Item 1.1.1.1| 6 |
| 4 | Item 1.2 | 1 |
| 7 | Item 1.2.1 | 4 |
| 2 | Item 2 | -1 |
| 5 | Item 2.1 | 2 |
| 8 | Item 2.2 | 2 |
+----+-------------+---------+
Sorry, I'm beginner to stored procedures. So I don't know how to get result like above table.
Thank you for reading
I think parent nodes must be before its child nodes.
Here is my query
DECLARE #SampleData AS TABLE( ID int, Name varchar(20) , ParentID int)
INSERT INTO #SampleData VALUES ( 1 ,' Item 1', -1 )
INSERT INTO #SampleData VALUES ( 2 ,' Item 2' , -1 )
INSERT INTO #SampleData VALUES ( 3 ,' Item 1.1' , 1 )
INSERT INTO #SampleData VALUES ( 4 ,' Item 1.2' , 1 )
INSERT INTO #SampleData VALUES ( 5 ,' Item 2.1' , 2 )
INSERT INTO #SampleData VALUES ( 6 ,' Item 1.1.1' , 4 )
INSERT INTO #SampleData VALUES ( 7 ,' Item 1.2.1' , 6 )
INSERT INTO #SampleData VALUES ( 8 ,' Item 2.2' , 2 )
;with cte as (
select t.Id, t.Name, t.ParentID, 1 as lev, t.Id AS RootId
from #SampleDAta t
where t.ParentID = -1
union all
select t.ID, t.Name,t.ParentID,cte.lev +1, cte.RootId
from cte
INNER JOIN #SampleDAta t on t.ParentID = cte.Id
)
SELECT c.Id, c.Name, c.ParentID FROM cte c
ORDER BY c.RootId, c.lev
OPTION (MAXRECURSION 0)
Result:
If you want to sort like a depth-search-tree, I can do it by a function
CREATE TABLE SampleData ( ID int, Name varchar(20) , ParentID int)
INSERT INTO SampleData VALUES ( 1 ,' Item 1', -1 )
INSERT INTO SampleData VALUES ( 2 ,' Item 2' , -1 )
INSERT INTO SampleData VALUES ( 3 ,' Item 1.1' , 1 )
INSERT INTO SampleData VALUES ( 4 ,' Item 1.2' , 1 )
INSERT INTO SampleData VALUES ( 5 ,' Item 2.1' , 2 )
INSERT INTO SampleData VALUES ( 6 ,' Item 1.1.1' , 3 )
INSERT INTO SampleData VALUES ( 7 ,' Item 1.2.1' , 4 )
INSERT INTO SampleData VALUES ( 8 ,' Item 2.2' , 2 )
Now Create function
CREATE FUNCTION DisplayTree
(
#RootId int
)
RETURNS
#result TABLE (
ID int, Name varchar(20) , ParentID int
)
AS
BEGIN
DECLARE #Temp AS TABLE
(
ID int, Name varchar(20) , ParentID int
)
INSERT INTO #Temp SELECT * FROM SampleData WHERE ParentID = #RootId
WHILE(EXISTS(SELECT 1 FROM #Temp t))
BEGIN
DECLARE #CurrentRootId int
SELECT TOP 1 #CurrentRootId = t.ID FROM #Temp t ORDER BY t.ID ASC
INSERT INTO #result SELECT * FROM #Temp t WHERE t.ID = #CurrentRootId
DELETE FROM #Temp WHERE ID = #CurrentRootId
INSERT INTO #result SELECT * FROM dbo.DisplayTree(#CurrentRootId)
END
RETURN ;
END
GO
AND Execute function
SELECT * FROM dbo.DisplayTree(-1)
You need to split the columns to N number of columns and then need to use Order By those columns.
Schema:
CREATE TABLE #TAB(ID INT, Name VARCHAR(20),ParentID INT)
INSERT INTO #TAB
SELECT 1, 'Item 1', -1
UNION ALL
SELECT 2, 'Item 2' , -1
UNION ALL
SELECT 3, 'Item 1.1' , 1
UNION ALL
SELECT 4, 'Item 1.2' , 1
UNION ALL
SELECT 5, 'Item 2.1' , 2
UNION ALL
SELECT 6, 'Item 1.1.1', 4
UNION ALL
SELECT 7, 'Item 1.2.1', 6
UNION ALL
SELECT 8, 'Item 2.2', 2
I converted Name column to XML and then I made columns from Tags using XML method value
;WITH CTE AS
(
SELECT
ID,
Name,
ParentID,
CAST('<M>'+REPLACE (REPLACE(Name,' ','.'),'.','</M><M>')+'</M>' AS XML)
AS XML_SPLT
FROM #TAB
)
SELECT
ID,
Name,
ParentID,
XML_SPLT.value('/M[1]', 'varchar(50)') As P0,
XML_SPLT.value('/M[2]', 'int') As P1,
XML_SPLT.value('/M[3]', 'int') As P2,
XML_SPLT.value('/M[4]', 'int') As P3
FROM CTE
ORDER BY P0,P1,P2,P3
GO
This will give you the order as you are expecting.
+----+------------+----------+------+----+------+------+
| ID | Name | ParentID | P0 | P1 | P2 | P3 |
+----+------------+----------+------+----+------+------+
| 1 | Item 1 | -1 | Item | 1 | NULL | NULL |
| 3 | Item 1.1 | 1 | Item | 1 | 1 | NULL |
| 6 | Item 1.1.1 | 4 | Item | 1 | 1 | 1 |
| 4 | Item 1.2 | 1 | Item | 1 | 2 | NULL |
| 7 | Item 1.2.1 | 6 | Item | 1 | 2 | 1 |
| 2 | Item 2 | -1 | Item | 2 | NULL | NULL |
| 5 | Item 2.1 | 2 | Item | 2 | 1 | NULL |
| 8 | Item 2.2 | 2 | Item | 2 | 2 | NULL |
+----+------------+----------+------+----+------+------+

How can i get the days between results dates for each seperate pat_id?

I'm looking to get the days between result dates for each patient: only looking at result dates where the result value is <90.00
;WITH patient_results AS (
SELECT * FROM (VALUES
(1, 'EA11AEE3-1D90-4602-9A37-0000007E2293', '85.10' ,'2015-12-11'),
(1, '27BCD3E4-2381-4139-B420-0000025B4113', '91.50' ,'2016-01-05'),
(1, 'D8969360-45D6-487B-AF94-0000035F78B0', '81.00' ,'2016-07-21'),
(5, '446E6413-442A-452A-BCF4-000006AA9896', '58.00' ,'2014-07-01'),
(5, '00305129-BC14-4A12-8368-00000AC04A9B', '53.00' ,'2014-12-13'),
(5, '96A67E53-2D6C-430B-A01F-00000AE4C37B', '42.80' ,'2015-02-01'),
(5, '7C330511-3E99-488C-AF5E-00000BDFA3FF', '54.00' ,'2015-07-01'),
(8, '62A2806A-4969-417A-B4DF-D547621CC594', '89.00' ,'2016-03-10'),
(8, '3B9F4E5B-3433-4F21-850A-FC2127A24B72', '92.60' ,'2016-06-30'),
(8, '1A2D780D-8C11-451C-8A64-6D49140B6232', '88.00' ,'2016-08-05')
) as t (pat_id, visit_id, result_value, result_date))
Based on the above looking to get something like this:
PAT_ID | VISIT_ID | RESULT_VALUE | RESULT_DATE| DAYSBETWEENRESULTDATES
1 | EA11AEE3-1D90-4602-9A37-0000007E2293 | 85.10 | 2015-12-11 | 0
1 | D8969360-45D6-487B-AF94-0000035F78B0 | 81.00 | 2016-07-21 | 223
5 | 446E6413-442A-452A-BCF4-000006AA9896 | 58.00 | 2014-07-01 | 0
5 | 00305129-BC14-4A12-8368-00000AC04A9B | 53.00 | 2014-12-13 | 165
5 | 96A67E53-2D6C-430B-A01F-00000AE4C37B | 42.80 | 2015-02-01 | 50
5 | 7C330511-3E99-488C-AF5E-00000BDFA3FF | 54.00 | 2015-07-01 | 150
8 | 62A2806A-4969-417A-B4DF-D547621CC594 | 89.00 | 2016-03-10 | 0
8 | 1A2D780D-8C11-451C-8A64-6D49140B6232 | 84.00 | 2016-08-05 | 148
I am using Sql Server 2012, Sql server management studio version 11.0.5058.0
Thank you.
Try this.
;WITH patient_results
AS
(
SELECT * FROM
(VALUES (1, 'EA11AEE3-1D90-4602-9A37-0000007E2293', '85.10' ,'2015-12-11'),
(1, '27BCD3E4-2381-4139-B420-0000025B4113', '91.50' ,'2016-01-05'),
(1, 'D8969360-45D6-487B-AF94-0000035F78B0', '81.00' ,'2016-07-21'),
(5, '446E6413-442A-452A-BCF4-000006AA9896', '58.00' ,'2014-07-01'),
(5, '00305129-BC14-4A12-8368-00000AC04A9B', '53.00' ,'2014-12-13'),
(5, '96A67E53-2D6C-430B-A01F-00000AE4C37B', '42.80' ,'2015-02-01'),
(5, '7C330511-3E99-488C-AF5E-00000BDFA3FF', '54.00' ,'2015-07-01'),
(8, '62A2806A-4969-417A-B4DF-D547621CC594', '89.00' ,'2016-03-10'),
(8, '3B9F4E5B-3433-4F21-850A-FC2127A24B72', '92.60' ,'2016-06-30'),
(8, '1A2D780D-8C11-451C-8A64-6D49140B6232', '88.00' ,'2016-08-05') )
as t (pat_id, visit_id, result_value, result_date))
SELECT *, ISNULL(DATEDIFF(DAY, LAG(result_date) OVER(PARTITION BY pat_id ORDER BY result_date), result_date), 0) as daysBetweenResultDates
FROM patient_results
WHERE result_value < 90.00
Result
pat_id visit_id result_value result_date DaysBetweenResultDates
1 EA11AEE3-1D90-4602-9A37-0000007E2293 85.10 2015-12-11 0
1 D8969360-45D6-487B-AF94-0000035F78B0 81.00 2016-07-21 223
5 446E6413-442A-452A-BCF4-000006AA9896 58.00 2014-07-01 0
5 00305129-BC14-4A12-8368-00000AC04A9B 53.00 2014-12-13 165
5 96A67E53-2D6C-430B-A01F-00000AE4C37B 42.80 2015-02-01 50
5 7C330511-3E99-488C-AF5E-00000BDFA3FF 54.00 2015-07-01 150
8 62A2806A-4969-417A-B4DF-D547621CC594 89.00 2016-03-10 0
8 1A2D780D-8C11-451C-8A64-6D49140B6232 88.00 2016-08-05 148
You can use OUTER APPLY to get previous values:
SELECT p.*,
ISNULL(DATEDIFF(DAY,t.result_date,p.result_date),0) AS DaysBetweenResultDates
FROM patient_results p
OUTER APPLY (
SELECT TOP 1 result_date
FROM patient_results
WHERE pat_id = p.pat_id and
result_date < p.result_date and
result_value < 90
ORDER BY result_date DESC) as t
WHERE p.result_value <90

In SQL getting the Max() of a Count() for a specific Group by

My script
SELECT ans.Questions_Id,ans.Answer_Numeric,ans.Option_Id, opt.Description, count(ans.Option_Id) as [Count]
FROM Answers ans
LEFT OUTER JOIN Questions que
ON ans.Questions_Id = que.Id
LEFT OUTER JOIN Options opt
ON ans.Option_Id = opt.Id
WHERE que.Survey_Id = 1
and ans.Questions_Id = 1
GROUP By ans.Questions_Id,ans.Answer_Numeric,ans.Option_Id, opt.Description
ORDER BY 2, 5 desc
I am trying to get the top number responses (Description) for each Answer_Numeric. The result at the moment looks like this:
| Questions_Id | Answer_Numeric | Option_Id | Description | Count
-----------------------------------------------------------------------
| 1 | 1 | 27 | Technology | 183
| 1 | 1 | 24 | Personal Items | 1
| 1 | 2 | 28 | Wallet / Purse | 174
| 1 | 2 | 24 | Personal Items | 3
| 1 | 2 | 26 | Spiritual | 1
| 1 | 3 | 24 | Personal Items | 53
| 1 | 3 | 25 | Food / Fluids | 5
| 1 | 3 | 26 | Spiritual | 5
| 1 | 3 | 27 | Technology | 1
| 1 | 3 | 28 | Wallet / Purse | 1
As from the example data from above I need it to look like this:
| Questions_Id | Answer_Numeric | Option_Id | Description | Count
-----------------------------------------------------------------------
| 1 | 1 | 27 | Technology | 183
| 1 | 2 | 28 | Wallet / Purse | 174
| 1 | 3 | 24 | Personal Items | 53
I am pretty sure that I need to have a max or something in my Having clause but everything I have tried has not worked. Would really appreciate any help on this.
You can use ROW_NUMBER:
SELECT Questions_Id, Answer_Numeric, Option_Id, Description, [Count]
FROM (
SELECT ans.Questions_Id,ans.Answer_Numeric,ans.Option_Id,
opt.Description, count(ans.Option_Id) as [Count],
ROW_NUMBER() OVER (PARTITION BY ans.Questions_Id, ans.Answer_Numeric
ORDER BY count(ans.Option_Id) DESC) AS rn
FROM Answers ans
LEFT OUTER JOIN Questions que
ON ans.Questions_Id = que.Id
LEFT OUTER JOIN Options opt
ON ans.Option_Id = opt.Id
WHERE que.Survey_Id = 1
and ans.Questions_Id = 1
GROUP By ans.Questions_Id,
ans.Answer_Numeric,
ans.Option_Id,
opt.Description) AS t
WHERE t.rn = 1
ORDER BY 2, 5 desc
Alternatively you can use RANK so as to handle ties, i.e. more than one rows per Questions_Id, Answer_Numeric partition sharing the same maximum Count number.
Use row_number():
SELECT *
FROM (SELECT ans.Questions_Id, ans.Answer_Numeric, ans.Option_Id, opt.Description,
count(*) as cnt,
row_number() over (partition by ans.Questions_Id, ans.Answer_Numeric
order by count(*) desc) as seqnum
FROM Answers ans LEFT OUTER JOIN
Questions que
ON ans.Questions_Id = que.Id LEFT OUTER JOIN
Options opt
ON ans.Option_Id = opt.Id
WHERE que.Survey_Id = 1 and ans.Questions_Id = 1
GROUP By ans.Questions_Id, ans.Answer_Numeric, ans.Option_Id, opt.Description
) t
WHERE seqnum = 1
ORDER BY 2, 5 desc;
we can get the same result set in different ways and I have taken sample data set you just merge your joins in this code
declare #Table1 TABLE
(Id int, Answer int, OptionId int, Description varchar(14), Count int)
;
INSERT INTO #Table1
(Id, Answer, OptionId, Description, Count)
VALUES
(1, 1, 27, 'Technology', 183),
(1, 1, 24, 'Personal Items', 1),
(1, 2, 28, 'Wallet / Purse', 174),
(1, 2, 24, 'Personal Items', 3),
(1, 2, 26, 'Spiritual', 1),
(1, 3, 24, 'Personal Items', 53),
(1, 3, 25, 'Food / Fluids', 5),
(1, 3, 26, 'Spiritual', 5),
(1, 3, 27, 'Technology', 1),
(1, 3, 28, 'Wallet / Purse', 1)
;
SELECT tt.Id, tt.Answer, tt.OptionId, tt.Description, tt.Count
FROM #Table1 tt
INNER JOIN
(SELECT OptionId, MAX(Count)OVER(PARTITION BY OptionId ORDER BY OptionId)AS RN
FROM #Table1
GROUP BY OptionId,count) groupedtt
ON
tt.Count = groupedtt.RN
WHERE tt.Count <> 5
GROUP BY tt.Id, tt.Answer, tt.OptionId, tt.Description, tt.Count
OR
select distinct Count, Description , Id , Answer from #Table1 e where 1 =
(select count(distinct Count ) from #Table1 where
Count >= e.Count and (Description = e.Description))

How to calculate SUM balance of all accounts in T-SQL?

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)

Sorting table in sql server

My table data like
id LedgerName
1 "105 AAA"
2 "102 sss"
3 "GGGG"
4 "107 BBB"
5 "BBBB"
6 "101 TTT"
i want sorting the Ledger like
6 "101 TTT"
2 "102 sss"
1 "105 AAA"
4 "107 BBB"
5 "BBBB"
3 "GGGG"
Normal Order by is not working.
i used split function to split for number based sorting in order by ..
how to fix this issue
With the data you have provided a regular order by on LedgerName is doing what you want.
Below is version that deals with data that is a bit more complicated.
SQL Fiddle
MS SQL Server 2008 Schema Setup:
create table YourTable
(
id int,
LedgerName varchar(20)
)
insert into YourTable values
(1, '105 AAA' ),
(2, '1020 sss' ),
(3, ' ' ),
(4, null ),
(5, '0' ),
(6, '999 sss' ),
(7, '9999 sss' ),
(8, 'GGGG' ),
(9, '107 BBB' ),
(10, 'BBBB' ),
(11, '101 TTT' )
Query 1:
select id,
LedgerName
from YourTable
order by case when patindex('%[^0-9]%', isnull(LedgerName, '')+' ') = 1 then 1 else 0 end,
cast(left(LedgerName, patindex('%[^0-9]%', LedgerName+' ')-1) as int),
LedgerName
Results:
| ID | LEDGERNAME |
-------------------
| 5 | 0 |
| 11 | 101 TTT |
| 1 | 105 AAA |
| 9 | 107 BBB |
| 6 | 999 sss |
| 2 | 1020 sss |
| 7 | 9999 sss |
| 4 | (null) |
| 3 | |
| 10 | BBBB |
| 8 | GGGG |

Resources