Make Total in Pivot Query - sql-server

Below is my query
SELECT UserName ,
TotCount ,
EntryDate
FROM #CandidateCount AS cc
and output of above query is
UserName TotCount EntryDate
--------------------------------
neelam 2 17/12/2013
neelam 1 18/12/2013
neelam 6 19/12/2013
snehal 7 17/12/2013
snehal 0 18/12/2013
snehal 2 19/12/2013
I have updated above query
SELECT *
FROM ( SELECT EntryDate ,
UserName ,
TotCount
FROM #CandidateCount AS tbl
) AS SourceTable PIVOT
( SUM(TotCount) FOR UserName IN ( [neelam], [snehal] ) )AS PivotTable
ORDER BY EntryDate DESC
and output of above query is
EntryDate neelam snehal
19/12/2013 6 2
18/12/2013 1 0
17/12/2013 2 7
Now i want to make total datewise like this
EntryDate neelam snehal Total
19/12/2013 6 2 8
18/12/2013 1 0 1
17/12/2013 2 7 9
How can i do this? Thanks.

I've put your data inside a CTE named Source to give you an example. With another CTE you can easily put your totals aside. You can later join these totals on both EntryDates:
WITH Source AS (
SELECT 'neelam' AS UserName, 2 AS TotCount, '17/12/2013' AS EntryDate UNION
SELECT 'neelam' AS UserName, 1 AS TotCount, '18/12/2013' AS EntryDate UNION
SELECT 'neelam' AS UserName, 6 AS TotCount, '19/12/2013' AS EntryDate UNION
SELECT 'snehal' AS UserName, 7 AS TotCount, '17/12/2013' AS EntryDate UNION
SELECT 'snehal' AS UserName, 0 AS TotCount, '18/12/2013' AS EntryDate UNION
SELECT 'snehal' AS UserName, 2 AS TotCount, '19/12/2013' AS EntryDate
)
, Totals AS (
SELECT EntryDate
, SUM(TotCount) AS Total
FROM Source
GROUP BY EntryDate
)
SELECT PivotTable.*
, Totals.Total
FROM ( SELECT EntryDate ,
UserName ,
TotCount
FROM Source AS tbl
) AS SourceTable PIVOT
( SUM(TotCount) FOR UserName IN ( [neelam], [snehal] ) )AS PivotTable
JOIN Totals ON PivotTable.EntryDate = Totals.EntryDate
ORDER BY PivotTable.EntryDate DESC
In your exact case:
WITH Totals AS (
SELECT EntryDate
, SUM(TotCount) AS Total
FROM #CandidateCount
GROUP BY EntryDate
)
SELECT PivotTable.*
, Totals.Total
FROM ( SELECT EntryDate ,
UserName ,
TotCount
FROM #CandidateCount AS tbl
) AS SourceTable PIVOT
( SUM(TotCount) FOR UserName IN ( [neelam], [snehal] ) )AS PivotTable
JOIN Totals ON PivotTable.EntryDate = Totals.EntryDate
ORDER BY PivotTable.EntryDate DESC

Related

how to select last rows where one certain value exist but not if it's in between

I have this table. With case#, Linenumber and code#.
case# Linenumber Code#
99L1HV 1 1510
99L1HV 2 4320
99PX58 1 1510
99PX58 2 4320
99PX58 3 4500
99PX59 1 1510
99PX59 2 918
99PX59 3 4320
How can I get the records with the last LineNumber per case# where code = 4320
The output should be like this
case# Linenumber Code
99L1HV 2 4320
99PX59 3 4320
Using ROW_NUMBER to get a number that's in the opposite order of the linenumber per case#.
Then the last lines will have RN = 1
SELECT [case#], Linenumber, [Code#]
FROM
(
SELECT [case#], Linenumber, [Code#],
ROW_NUMBER() OVER (PARTITION BY [case#] ORDER BY Linenumber DESC) AS RN
FROM yourtable
) q
WHERE RN = 1
AND [Code#] = 4320
ORDER BY [case#];
Or the more concise version.
Using a TOP 1 WITH TIES in combination with an ORDER BY ROW_NUMBER.
SELECT *
FROM
(
SELECT TOP 1 WITH TIES [case#], Linenumber, [Code#]
FROM yourtable
ORDER BY ROW_NUMBER() OVER (PARTITION BY [case#] ORDER BY Linenumber DESC)
) q
WHERE [Code#] = 4320
ORDER BY [case#];
cte is to generate a running number by case#. rn = 1 will be the last row for each case#
; with cte as
(
select *, rn = row_number() over (partition by [case#] order by linenumber desc)
from yourtable
)
select *
from cte
where rn = 1
and [code#] = 4320
declare #t table (
CaseNumber varchar(10),
LineNumber int,
CodeNumber int
);
-- Filling the table with data, skipped
select t.*
from #t t
where t.CodeNumber = 4320
and not exists (
select 0 from #t x
where x.CaseNumber = t.CaseNumber
and x.LineNumber > t.LineNumber
);
with cte as
(select case#, max(linenumber)
from source_table
group by case#)
select t1.*
from source_table t1 inner join cte t2
on t1.case# = t2.case# and t1.linenumber = t2.linenumber
where t1.Code# = 4320

Update null values by value in same column

I have a table in MS SQL Server, where are some null values in column "value"
Group ID Value
A 1 10
A 2
A 3
A 4 40
B 1
B 2 20
B 3 30
B 4
I want to update null values by not null in the same group with with the first higher ID, or if there is not any higher in same group, first lower. So the result should look like this.
Group ID Value
A 1 10
A 2 40
A 3 40
A 4 40
B 1 20
B 2 20
B 3 30
B 4 30
Thanks!
You can use windowed version of SUM function in order to determine islands of NULL valued records along with the record having the higher ID in the same group:
SELECT [Group], ID, Value,
SUM(CASE WHEN Value IS NULL THEN 0 ELSE 1 END) OVER
(PARTITION BY [Group] ORDER BY ID DESC) AS grp
FROM mytable
Output:
Group ID Value grp
-----------------------
A 4 40 1
A 3 30 2
A 2 NULL 2
A 1 NULL 2
B 4 40 1
B 3 NULL 1
B 2 20 2
B 1 10 3
You can now wrap the above query in a CTE and use another CTE to do the update:
;WITH CTE AS (
SELECT [Group], ID, Value,
SUM(CASE WHEN Value IS NULL THEN 0 ELSE 1 END) OVER
(PARTITION BY [Group] ORDER BY ID DESC) AS grp
FROM mytable
), ToUpdate AS (
SELECT [Group], ID, Value,
MAX(Value) OVER (PARTITION BY [Group], grp) AS group_value
FROM CTE
)
UPDATE ToUpdate
SET Value = group_value
WHERE Value IS NULL
Demo here
Edit:
The above query doesn't handle the edge case where the very last record within a Group slice is NULL. To handle this case as well you can use the following query:
;WITH CTE AS (
SELECT [Group], ID, Value,
SUM(CASE WHEN Value IS NULL THEN 0 ELSE 1 END) OVER
(PARTITION BY [Group] ORDER BY ID DESC) AS grp,
SUM(CASE WHEN Value IS NULL THEN 0 ELSE 1 END) OVER
(PARTITION BY [Group] ORDER BY ID) AS grp2
FROM mytable
), ToUpdate AS (
SELECT [Group], ID, Value,
MAX(Value) OVER (PARTITION BY [Group], grp) AS group_value,
MAX(Value) OVER (PARTITION BY [Group], grp2) AS group_value2
FROM CTE
)
UPDATE ToUpdate
SET Value = COALESCE(group_value, group_value2)
WHERE Value IS NULL
Demo here
Please try this-
DATA GENERATION
DECLARE #T TABLE
(
GroupCd CHAR(1),
Id INT,
Value INT
)
INSERT INTO #T
VALUES('A',1,10),
('A',2,NULL),
('A',3,NULL),
('A',4,40),
('B',1,NULL),
('B',2,20),
('B',3,30),
('B',4,NULL)
SOLUTION
UPDATE a
SET a.Value = b.Value
FROM #T a
INNER JOIN
(
SELECT a.GroupCd,a.Id,Coalesce(a.Value,z.Value,z1.Value) Value
FROM #T a
OUTER APPLY
(
SELECT TOP 1 Value
FROM #T b
WHERE a.GroupCd = b.GroupCd
AND b.Value IS NOT NULL AND a.Id < b.Id
ORDER BY Id
)z
OUTER APPLY
(
SELECT TOP 1 Value
FROM #T b
WHERE a.GroupCd = b.GroupCd
AND b.Value IS NOT NULL AND a.Id > b.Id
ORDER BY Id DESC
)z1
)b ON a.GroupCd = b.GroupCd AND a.Id = b.Id
SELECT * FROM #T
OUTPUT
GroupCd Id Value
------- ----------- -----------
A 1 10
A 2 40
A 3 40
A 4 40
B 1 20
B 2 20
B 3 30
B 4 30
(8 rows affected)
You Can try This simple Method
DECLARE #T TABLE
(
GroupCd CHAR(1),
Id INT,
Value INT
)
INSERT INTO #T
VALUES('A',1,NULL),
('A',2,NULL),
('A',3,30),
('A',4,40),
('B',1,10),
('B',2,20),
('B',3,NULL),
('B',4,40)
SELECT
*,
NewVal = COALESCE(Value,(SELECT TOP 1 Value FROM #T WHERE GroupCd = T.GroupCd AND Id > T.Id AND Value IS NOT NULL ORDER BY Id ASC))
FROM #T T
My Result
update MY_TABLE set [value] = [newValue] from (
select [Group] [newGroup],
[Value] [newValue]
from (
select [Group], [Value],
row_number() over (partition by [group] order by [Id] desc) [rn]
from MY_TABLE
where [Value] is not null
) [a] where [rn] = 1
) where [Group] = [newGroup] and [Value] is null

SQL Server pivot query returns only one row

My data is like this:
ID ColumnName columnnvalue
-----------------------------
13 seraialnew tester name
13 serial 123123
13 seraialnew am444
13 serial 33333
SELECT id,seraialnew,serial
FROM (
SELECT id,columnnvalue, ColumnName FROM dbo.formValues
) cols
PIVOT(
MAX(id)
FOR ColumnName IN(seraialnew,serial)
)p
and get single row result instead multiple
If you got same id there will be only one string, so I changed the id's for 2 of your rows:
;WITH formValues AS (
SELECT *
FROM (VALUES
(13,'seraialnew','tester name'),
(13,'serial','123123'),
(14,'seraialnew','am444'),
(14,'serial','33333')
) as t(ID, ColumnName, columnnvalue)
)
SELECT id,seraialnew,serial
FROM (
SELECT id,columnnvalue, ColumnName
FROM formValues
) cols
PIVOT(
MAX(columnnvalue)
FOR ColumnName IN(seraialnew,serial)
)p
Will give you:
id seraialnew serial
13 tester name 123123
14 am444 33333
Another way:
SELECT *
FROM (
SELECT id,
columnnvalue,
ColumnName+CAST(ROW_NUMBER() OVER (PARTITION BY ColumnName ORDER BY ColumnName) as nvarchar(1)) as ColumnName
FROM formValues
) cols
PIVOT(
MAX(columnnvalue)
FOR ColumnName IN(seraialnew1,serial1,seraialnew2,serial2)
)p
With same id = 13 will give you this:
id seraialnew1 serial1 seraialnew2 serial2
13 am444 33333 tester name 123123

T-SQL Cumulative Count Reset per Month

I need to count cumulative count in T-SQL. This can be done as:
WITH DATASET AS (SELECT '2014-01-28' AS [DATE], 1 AS [COUNT]
UNION
SELECT '2014-01-29' AS [DATE], 5 AS [COUNT]
UNION
SELECT '2014-01-30' AS [DATE], 15 AS [COUNT]
UNION
SELECT '2014-01-31' AS [DATE], 4 AS [COUNT]
UNION
SELECT '2014-02-01' AS [DATE], 7 AS [COUNT]
UNION
SELECT '2014-02-02' AS [DATE], 1 AS [COUNT]
)
, CTE AS (SELECT *
,ROW_NUMBER() OVER (ORDER BY [DATE]) ROWNUM
FROM DATASET
)
SELECT CTE1.[DATE]
,CTE1.[COUNT]
,SUM(CTE2.[COUNT]) AS CUM_CNT
FROM CTE CTE1
JOIN CTE CTE2 ON CTE2.ROWNUM <= CTE1.ROWNUM
GROUP BY CTE1.[DATE]
,CTE1.[COUNT]
That returns:
DATE COUNT CUM_CNT
2014-01-28 1 1
2014-01-29 5 6
2014-01-30 15 21
2014-01-31 4 25
2014-02-01 7 32
2014-02-02 1 33
But I want to reset the cumulative count per each month so the data returned should be:
DATE COUNT CUM_CNT
2014-01-28 1 1
2014-01-29 5 6
2014-01-30 15 21
2014-01-31 4 25
2014-02-01 7 7
2014-02-02 1 8
Is it possible to achieve this in T-SQL? How?
As of SQL Server 2012+ you can use window version of SUM to calculate running totals. You just have to PARTITION by YEAR([DATE]), MONTH([DATE]) to get the expected result:
WITH DATASET AS (
SELECT '2014-01-28' AS [DATE], 1 AS [COUNT]
UNION
SELECT '2014-01-29' AS [DATE], 5 AS [COUNT]
UNION
SELECT '2014-01-30' AS [DATE], 15 AS [COUNT]
UNION
SELECT '2014-01-31' AS [DATE], 4 AS [COUNT]
UNION
SELECT '2014-02-01' AS [DATE], 7 AS [COUNT]
UNION
SELECT '2014-02-02' AS [DATE], 1 AS [COUNT]
)
SELECT [DATE], [COUNT],
SUM([COUNT]) OVER (PARTITION BY YEAR([DATE]), MONTH([DATE])
ORDER BY [DATE]) AS CUM_CNT
FROM DATASET
SQL Fiddle Demo
SELECT '2014-01-28' AS [DATE], 1 AS [COUNT]
UNION
SELECT '2014-01-29' AS [DATE], 5 AS [COUNT]
UNION
SELECT '2014-01-30' AS [DATE], 15 AS [COUNT]
UNION
SELECT '2014-01-31' AS [DATE], 4 AS [COUNT]
UNION
SELECT '2014-02-01' AS [DATE], 7 AS [COUNT]
UNION
SELECT '2014-02-02' AS [DATE], 1 AS [COUNT]
)
Select C2.date,sum(c1.count) as COUNT, sum(distinct c2.count) AS CUML_COUNT from DATASET C1
JOIN DATASET C2 ON month(C1.date) = month(C2.date) and C1.Date <= C2.Date
group by C2.date

Show all RowNumber records for duplicates?

I have a table of string resources :
;WITH cte AS
(
SELECT 1 AS id , 'john' AS name, 10 AS age
UNION
SELECT 2 AS id , 'john' AS name, 10 AS age
UNION
SELECT 3 AS id , 'john' AS name, 12 AS age
UNION
SELECT 4 AS id , 'paul' AS name, 6 AS age
UNION
SELECT 5 AS id , 'paul ' AS name, 6 AS age
UNION
SELECT 6 AS id , 'paul different' AS name, 7 AS age
UNION
SELECT 7 AS id , 'ringo' AS name, 2 AS age
)
So the name "john" has age of 10.
Later on , someone else (not me) also added "john" with age 10.
So I want to clean all duplicates.
But that's not the problem. Before I delete I want to see all duplicates.
So I did this :
SELECT *
FROM (
SELECT ID,
name,
age,
ROW_NUMBER() OVER(PARTITION BY name, age ORDER BY id) AS rn
FROM cte
) a WHERE a.rn>1
ORDER BY
name,
age,
a.rn
Result :
Which basically shows me duplicates. But I want to see also where rn=1 only if there's more version for the current value.
Question
In other words : How can I enhance my query so :
Show all versions for a record ( all row numbers , rn) only if there are versions for this record
Desired result :
ID name age rn
1 john 10 1
2 john 10 2
4 paul 6 1
5 paul 6 2
Sql online - demo
NB I know i can do it with rescanning the table for the same name and age . bUt I thought if there's more elegant way of doing it.
Use exists operator to find the name which are duplicated. Try this.
;WITH cte AS
(
SELECT 1 AS id , 'john' AS name, 10 AS age
UNION
SELECT 2 AS id , 'john' AS name, 10 AS age
UNION
SELECT 3 AS id , 'john' AS name, 12 AS age
UNION
SELECT 4 AS id , 'paul' AS name, 6 AS age
UNION
SELECT 5 AS id , 'paul ' AS name, 6 AS age
UNION
SELECT 6 AS id , 'paul different' AS name, 7 AS age
UNION
SELECT 7 AS id , 'ringo' AS name, 2 AS age
)
, cte1
AS (SELECT ID,
name,
age,
Row_number() OVER(PARTITION BY name, age ORDER BY id) AS rn
FROM cte)
SELECT *
FROM cte1 a
WHERE EXISTS (SELECT 1
FROM cte1 b
WHERE a.name = b.name and a.age=b.age
AND b.rn > 1)
ORDER BY name, age, a.rn
or use Inner Join
SELECT a.id,a.name,a.age
FROM cte1 a
JOIN cte1 b
ON a.name = b.name
AND a.age = b.age
AND b.rn > 1
ORDER BY a.name, a.age, a.rn
Or To do it in single table scan use Dense_Rank plus window function
;WITH cte AS
(
SELECT 1 AS id , 'john' AS name, 10 AS age
UNION
SELECT 2 AS id , 'john' AS name, 10 AS age
UNION
SELECT 3 AS id , 'john' AS name, 12 AS age
UNION
SELECT 4 AS id , 'paul' AS name, 6 AS age
UNION
SELECT 5 AS id , 'paul ' AS name, 6 AS age
UNION
SELECT 6 AS id , 'paul different' AS name, 7 AS age
UNION
SELECT 7 AS id , 'ringo' AS name, 2 AS age
)
, cte1
AS (SELECT ID,
name,
age,
count(age) over (partition by name,age) cnt,
dense_rank() OVER(PARTITION BY name ORDER BY age) AS rn
FROM cte)
SELECT *
FROM cte1
WHERE rn = 1
AND cnt > 1

Resources