I would like to combine ROLLUP with PIVOT - is that an option? - sql

I have been using
SELECT
Author,
ISNULL(MAX(CASE Status WHEN 'Duplicate' THEN NumDocs END),'') AS Duplicate,
ISNULL(MAX(CASE Status WHEN 'Failure' THEN NumDocs END),'') AS Failure,
ISNULL(MAX(CASE Status WHEN 'Rejected' THEN NumDocs END),'') AS Rejected,
ISNULL(MAX(CASE Status WHEN 'Success' THEN NumDocs END),'') AS Success,
ISNULL(MAX(CASE Status WHEN 'TOTAL' THEN NumDocs END),'') AS TOTAL
FROM
(SELECT
CASE WHEN (GROUPING(Author)=1) THEN 'ALL'
ELSE ISNULL(Author,'UNKNOWN') END AS Author,
CASE WHEN (GROUPING(Status )=1) THEN 'TOTAL'
ELSE ISNULL(Status ,'UNKNOWN') END AS [Status],
COUNT(Status) AS NumDocs
FROM
tbl_Document D
LEFT JOIN
tbl_Status S
ON
D.status_id = S.status_id
GROUP BY
Author,
Status
WITH ROLLUP) BASE
GROUP BY
Author
To transform:
[Author] [Status]
Alan SUCCESS
Bob FAILURE
Bob SUCCESS
Charles SUCCESS
Dave FAILURE
Dave DUPLICATE
TO:
[Author] [SUCCESS] [FAILURE] [DUPLICATE] [TOTALS]
Alan 1 0 0 1
Bob 1 1 0 2
Charles 1 0 0 1
Dave 0 1 1 2
TOTAL 3 2 1 6
I can get close to this output using a PIVOT statement, but I'm not sure how to get the TOTAL row/column?
SELECT
*
FROM
(SELECT Author, status_id FROM tbl_Document) d
PIVOT
(COUNT(status_id) FOR status_id IN ([1],[3],[5],[6])) p
Gives:
[Author] [SUCCESS] [FAILURE] [DUPLICATE]
Alan 1 0 0
Bob 1 1 0
Charles 1 0 0
Dave 0 1 1
I'm guessing I need to put the ROLLUP into a subquery somewhere...?

You didn't post the table schema, so I tried to infer it. I started with the input you gave (see the comment in the innermost SELECT), so you should be able to adapt this to your actual schema. I included an extra author without any documents, because I figured you'd want to see those in the final report output. It's trivial to exclude those authors.
DECLARE #Status table
(
Id int NOT NULL,
Status nvarchar(50) NOT NULL
)
DECLARE #Authors table
(
Id int NOT NULL,
Name nvarchar(50) NOT NULL
)
DECLARE #Documents table
(
Id int NOT NULL,
AuthorId int NOT NULL,
StatusId int NOT NULL
)
INSERT INTO #Status VALUES (1, 'Duplicate')
INSERT INTO #Status VALUES (2, 'Failure')
INSERT INTO #Status VALUES (3, 'Rejected')
INSERT INTO #Status VALUES (4, 'Success')
INSERT INTO #Authors VALUES (1, 'Alan')
INSERT INTO #Authors VALUES (2, 'Bob')
INSERT INTO #Authors VALUES (3, 'Charles')
INSERT INTO #Authors VALUES (4, 'Dave')
INSERT INTO #Authors VALUES (5, 'Tom') -- Test for authors without documents
INSERT INTO #Documents VALUES (1, 1, 4)
INSERT INTO #Documents VALUES (2, 2, 2)
INSERT INTO #Documents VALUES (3, 2, 4)
INSERT INTO #Documents VALUES (4, 3, 4)
INSERT INTO #Documents VALUES (5, 4, 2)
INSERT INTO #Documents VALUES (6, 4, 1)
SELECT
(CASE WHEN GROUPING(Name) = 1 THEN 'Total' ELSE Name END) AS Author,
SUM(Duplicate) AS Duplicate,
SUM(Failure) AS Failure,
SUM(Rejected) AS Rejected,
SUM(Success) AS Success,
SUM(Duplicate + Failure + Rejected + Success) AS Total
FROM
(
SELECT
Name,
(CASE WHEN Status = 'Duplicate' THEN 1 ELSE 0 END) AS Duplicate,
(CASE WHEN Status = 'Failure' THEN 1 ELSE 0 END) AS Failure,
(CASE WHEN Status = 'Rejected' THEN 1 ELSE 0 END) AS Rejected,
(CASE WHEN Status = 'Success' THEN 1 ELSE 0 END) AS Success
FROM
(
-- Original input
SELECT
a.Name,
s.Status
FROM #Authors a
LEFT OUTER JOIN #Documents d ON d.AuthorId = a.Id
LEFT OUTER JOIN #Status s ON d.StatusId = s.Id
) i
) j
GROUP BY Name WITH ROLLUP
Output:
Author Duplicate Failure Rejected Success Total
Alan 0 0 0 1 1
Bob 0 1 0 1 2
Charles 0 0 0 1 1
Dave 1 1 0 0 2
Tom 0 0 0 0 0
Total 1 2 0 3 6

Related

T-SQL query suggestion

CREATE TABLE #Emp
(
ID int,
Name varchar(100)
)
INSERT INTO #Emp
VALUES (1, 'AAA'), (2, 'BBB'), (3, 'CCC')
CREATE TABLE #Task
(
EmpID int,
TaskName varchar(100),
[Hours] int
)
INSERT INTO #Task
VALUES (1, 'Task-1', 2), (1, 'Task-2', 4), (1, 'Task-5', 3),
(2, 'Task-3', 2), (2, 'Task-4', 4), (2, 'Task-5', 3),
(3, 'Task-1', 2), (3, 'Task-1', 4), (3, 'Task-1', 6),
(3, 'Task-2', 3), (3, 'Task-6', 1)
#Emp
ID Name
--------
1 AAA
2 BBB
3 CCC
#Task:
EmpID TaskName Hours
-------------------------
1 Task-1 2
1 Task-2 4
1 Task-5 3
2 Task-3 2
2 Task-4 4
2 Task-5 8
3 Task-1 2
3 Task-1 4
3 Task-1 6
3 Task-2 3
3 Task-6 1
For each employee I need to get the sum of hours for (Task-1 and Task-2) and for Task-5
Something like below
Name PrepHours(Task-1 + Task-2) ReviewHours(Task-5)
-------------------------------------------------------
AAA 6 3
BBB 0 8
CCC 15 0
I tried the query shown here, but it fails with error column #Task.TaskName is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
SELECT
Name, PrepHours, ReviewHours
FROM
#Emp AS E
JOIN
(SELECT
empid,
CASE
WHEN Taskname IN ('Task-1','Task-2')
THEN SUM(Hours)
ELSE 0
END AS 'PrepHours',
CASE
WHEN Taskname IN ('Task-5')
THEN SUM(Hours)
ELSE 0
END AS 'ReviewHours'
FROM
#Task
WHERE
Taskname IN ('Task-1', 'Task-2', 'Task-5')
GROUP BY
empid) AS t ON E.id = t.empid
ORDER BY
Name
So if I add the Taskname in the Group by it provides multiple rows for each. I need one row for each employee. Need help please.
Name PrepHours ReviewHours
-------------------------------
AAA 2 0
AAA 4 0
AAA 0 3
BBB 0 8
CCC 12 0
CCC 3 0
You can create your totals using a conditional case espression in a cross apply
select e.name, t.*
from #emp e
cross apply (
select
Sum(case when taskname in ('task-1','task-2') then hours else 0 end) PrepHours,
Sum(case when taskname ='Task-5' then hours else 0 end) ReviewHours
from #task t
where t.EmpId=e.Id
)t
You can make the original query work by moving the Sum outside the case statement:
SELECT [Name],
PrepHours,
ReviewHours
FROM #Emp AS E
JOIN (SELECT empid,
Sum( CASE
WHEN Taskname IN ( 'Task-1', 'Task-2' ) THEN [Hours]
ELSE 0
END) AS 'PrepHours',
sum(CASE
WHEN Taskname IN ( 'Task-5' ) THEN [Hours]
ELSE 0
END) AS 'ReviewHours'
FROM #Task
WHERE Taskname IN ( 'Task-1', 'Task-2', 'Task-5' )
GROUP BY empid) AS t
ON E.id = t.empid
ORDER BY Name

Retrieve different customer status

I want to write a query to retrieve the result from the table.
When the name all my statuses are completed to show me completed. When the status has completed but there is another status such as in progress or created then show only the other status
CustomName
STATUS
order
Ivan Ivanov
completed
1
Stoqn Stoqnov
completed
1
Dimityr Ivanov
completed
1
Ivan Ivanov
completed
2
Dimityr Ivanov
completed
2
Ivan Ivanov
inprocess
2
Stoqn Stoqnov
completed
2
Dimityr Ivanov
completed
3
Dimityr Ivanov
created
4
Stoqn Stoqnov
completed
3
Ivan Ivanov
completed
4
Stoqn Stoqnov
completed
4
Expected result
Ivan Ivanov
inprocess
Dimityr Ivanov
created
Stoqn Stoqnov
completed
Query:
SELECT distinct CustomName,
(CASE WHEN [STATUS] ='COMPLETED' THEN 'completed'
WHEN [STATUS] ='inprocess' THEN 'inprocess'
WHEN [STATUS] ='created' THEN 'created' END ) AS [STATUS]
from [dbo].[Customers]
You can do something like the following where you count how many of each status there are per customer and then choose in a priority order which to display.
declare #Test table (CustomName varchar(32), [STATUS] varchar(32), [Order] int)
insert into #Test (CustomName, [STATUS], [Order])
values
('Ivan Ivanov', 'completed', 1),
('Stoqn Stoqnov', 'completed', 1),
('Dimityr Ivanov', 'completed', 1),
('Ivan Ivanov', 'completed', 2),
('Dimityr Ivanov', 'completed', 2),
('Ivan Ivanov', 'inprocess', 2),
('Stoqn Stoqnov', 'completed', 2),
('Dimityr Ivanov', 'completed', 3),
('Dimityr Ivanov', 'created', 4),
('Stoqn Stoqnov', 'completed', 3),
('Ivan Ivanov', 'completed', 4),
('Stoqn Stoqnov', 'completed', 4);
with cte as (
select CustomName
, sum(case when [status] = 'completed' then 1 else 0 end) over (partition by CustomName) Completed
, sum(case when [status] = 'created' then 1 else 0 end) over (partition by CustomName) Created
, sum(case when [status] = 'inprocess' then 1 else 0 end) over (partition by CustomName) InProcess
from #Test
)
select CustomName
-- This logic could be more complex if desired
, case when InProcess > 0 then 'In Process' when Created > 0 then 'Created' else 'Completed' end
from cte
group by CustomName, Completed, Created, InProcess;
Returns:
CustomName
Status
Dimityr Ivanov
Created
Ivan Ivanov
In Process
Stoqn Stoqnov
Completed
Note: Providing the DDL+DML as I have shown, makes it much easier for people to assist.
I don't think this needs window functions or cross joins, just a simple GROUP BY with conditional counts
SELECT
CustomName,
CASE WHEN COUNT(CASE WHEN [status] = 'created' THEN 1 END) > 0 THEN 'Created'
WHEN COUNT(CASE WHEN [status] = 'inprocess' THEN 1 END) > 0 THEN 'In Process'
ELSE 'Completed' END
FROM YourTable t
GROUP BY t.CustomName;
Try the following:
The idea is to use cte to find all Customer Name who contain different type of status
and then use UNION ALL to find all Customer Name who ONLY contains "Completed" status
;WITH cte AS (
SELECT DISTINCT CustomName, [STATUS]
FROM [dbo].[Customers]
WHERE [STATUS] <> 'Completed'
)
SELECT *
FROM cte
UNION ALL
SELECT DISTINCT CustomName, [STATUS]
FROM [dbo].[Customers]
WHERE CustomName NOT IN (SELECT CustomName FROM cte)

Count and GROUP values from a CASE expression

Trying to count the total number of wins and total times a horse finished in the top 6 and GROUP by the order of favouritism in a horse racing market.
I am referencing this answer since it is looking for a similar outcome Count the occurrences of DISTINCT values
I have tried this expression but when executed, it returns the same number in both case columns
ie;
SPFav WinsByFavouritism PlaceByFavouritism
1 4143 4143
2 3963 3963
3 3853 3853
This is the code I am running - what is causing this?
SELECT SPFav,
COUNT(CASE WHEN FinishingPosition = 1 THEN 1 ELSE 0 END) as WinsByFavouritism,
COUNT(CASE WHEN FinishingPosition <= 6 THEN 1 ELSE 0 END) as PlaceByFavouritism
FROM [NRaceHistory].[dbo].[EachWayBetting]
GROUP BY SPFav
ORDER BY SPFav ;
Working with the first comment this would give the following possible solution.
Sample data
create table EachWayBetting
(
SPFav int,
FinishingPosition int
);
insert into EachWayBetting (SPFav, FinishingPosition) values
(1, 1),
(1, 2),
(1, 2),
(1, 9),
(2, 7),
(2, 8),
(2, 2),
(2, 1);
Solution
SELECT SPFav,
COUNT(CASE WHEN FinishingPosition = 1 THEN 1 END) as WinsByFavouritism,
COUNT(CASE WHEN FinishingPosition <= 6 THEN 1 END) as PlaceByFavouritism
FROM EachWayBetting
GROUP BY SPFav
ORDER BY SPFav
Result
SPFav WinsByFavouritism PlaceByFavouritism
----- ----------------- ------------------
1 1 3
2 1 2
Fiddle
I believe you should replace COUNT in your query with SUM. i.e. it should be:
SELECT SPFav,
SUM(CASE WHEN FinishingPosition = 1 THEN 1 ELSE 0 END) as WinsByFavouritism,
SUM(CASE WHEN FinishingPosition <= 6 THEN 1 ELSE 0 END) as PlaceByFavouritism
FROM [NRaceHistory].[dbo].[EachWayBetting]
GROUP BY SPFav
ORDER BY SPFav;

How to achieve this output?

I have a table with data like this :
create table test (transferID int, customerNumber varchar(10), txnstatus int);
insert into test
values
(1, 1001, 1),
(2, 1001, 2),
(3, 1001, 1),
(4, 1002, 2),
(5, 1002, 1),
(6, 1002, 2),
(7, 1002, 1),
(8, 1002, 1),
(9, 1003, 2),
(10, 1003, 1),
(11, 1003, 1),
(12, 1003, 1),
(13, 1003, 1),
(14, ' ', 1),
(15, ' ', 2),
(16, NULL, 2);
and the excepted output is to display the fields with customer number, total number of txns for each customer, successfulTxns, failedTxns. Note that:
txnStatus 1 and 2 represent "success" and "fail" respectively.
customer number may be empty or NULL in some cases like last three rows
This is how I tried, but didn't get the excepted result
select customerNumber,
count(*) over (partition by 1) as TotalTxns,
case when txnstatus = 1 then count(txnstatus) else 0 end as successFulTrxn,
case when txnstatus = 2 then count(txnstatus) else 0 end as failedFulTrxn
from test
group by customerNumber, txnstatus
I expect the output to be:
CustNumber TotalTxns SuccessFulTxns FailedTxns
1001 3 2 1
1002 5 3 2
1003 5 4 1
2 1 1
NULL 1 0 1
Your logic is somewhat correct, you just need to put the CASE expression inside COUNT, not the other way round:
SELECT customerNumber
, COUNT(*) AS TotalTxns
, COUNT(CASE WHEN txnstatus = 1 THEN 1 END) AS SuccessFulTxns
, COUNT(CASE WHEN txnstatus = 2 THEN 1 END) AS FailedTxns
FROM test
GROUP BY customerNumber
Note that there is not such thing as empty INT. Empty strings/whitespace becomes 0 when converted to INT.
Instead of blank I inserted 0 as customerNumber is int. If you want the same order as expected result, If you need 0 and NULL at the end, you can use conditional orderby.
SELECT customerNumber
, COUNT(*) AS TotalTxns
, COUNT(CASE WHEN txnstatus = 1 THEN 1 END) AS successFulTrxn
, COUNT(CASE WHEN txnstatus = 2 THEN 1 END) AS failedFulTrxn
FROM test
GROUP BY customerNumber
ORDER BY CASE WHEN customerNumber IS NULL THEN 100000
WHEN customerNumber = 0 THEN 99999
ELSE customerNumber END
Conditional aggregation is the way to achieve this :
select customerNumber, count(transferID) as TotalTxns,
sum(case when txnstatus = 1 then 1 else 0 end) as successFulTrxn,
sum(case when txnstatus = 2 then 1 else 0 end) as failedFulTrxn
from test t
group by customerNumber;
If you have to do a grouping action on txnstatus, you obviously can't include that field in the grouping list.
Furthermore, you can't do the count inside the case, you have to do it outside, and you don't have to count the status, because if the status is '2', the result will be clearly uncorrect.
I would approach the problem as below.
select customerNumber, count(*) as TotalTxns,
sum(case when txnstatus = 1 then 1 else 0 end) as successFulTrxn,
sum(case when txnstatus = 2 then 1 else 0 end) as failedFulTrxn
from test
group by customerNumber
It gives to me the results you are looking for.

group by - counting rows and if not existing add row with 0 value

I know I already asked a similar question, but here I start from zero... Without giving any query I tried so I don't influance you.
If this is my table:
status PGID nvarchar5 nvarchar10 CatId tp_ID isActive
IT NULL Information technology NULL 1 1 1
HR NULL Human Recource NULL 1 2 1
FIN NULL Finance NULL 1 3 1
New 1 NULL 1354 2 10001 1
New 1 NULL 464 2 10002 1
New 1 NULL 13465 2 10003 1
Active 1 NULL 79846 2 10004 1
Deleted 1 NULL 132465 2 10005 1
New 2 NULL 79847 2 10006 1
New 2 NULL 341 2 10007 1
Deleted 2 NULL 465 2 10008 1
Deleted 2 NULL 132 2 10009 1
Deleted 2 NULL 465 2 10010 1
Deleted 2 NULL 1 2 10011 1
New 3 NULL 465 2 10012 1
New 3 NULL 1465 2 10013 1
New 3 NULL 132 2 10014 1
NULL NULL NULL NULL 3 20136 1
NULL NULL NULL NULL 4 22165 1
NULL NULL NULL NULL 3 24566 1
NULL NULL NULL NULL 10 24566 1
What should be the query if I want a result like this:
status PGID nvarchar5 total
new 1 Information technology 3
active 1 Information technology 1
deleted 1 Information technology 1
new 2 Human Recource 2
active 2 Human Recource 0
deleted 2 Human Recource 4
new 3 Finance 3
active 3 Finance 0
deleted 3 Finance 0
Or is this not possible?
Edit:
If you want to see what I've tried: wrong number in count()
Update:
How I calculate my total:
I won't bother going into how bad the data looks, column names etc and will presume it's a case of 'this is what I have to work with'. So given the data try
SELECT t.Status, t.Department_ID, t.Department, COALESCE(s.Total, 0) AS Total
FROM (
SELECT nvarchar5 AS Department, tp_ID AS Department_ID, Status
FROM My_Table,
(SELECT 'new' AS Status
UNION ALL
SELECT 'active' AS Status
UNION ALL
SELECT 'deleted' AS Status ) AS m
WHERE tp_ID IN (1,2,3)
) AS t
LEFT JOIN (
SELECT status AS Status, PGID AS Department_ID, COUNT(1) AS Total
FROM My_Table
WHERE PGID IS NOT NULL
GROUP BY Status, PGID
) AS s
ON t.Status = s.Status
AND t.Department_ID = s.Department_ID
That is not the answer, but I think this can help you too:
declare #t as table (
num varchar(50) null,
t varchar(50) null,
ord int not null,
pgid int null
)
insert into #t values ('IT', 'Info', 1 , null)
insert into #t values ('HR' , 'Human', 2 , null)
insert into #t values ('FIN', 'Finance', 3 , null )
insert into #t values ('New' , NULL, 10001, 1 )
insert into #t values ('New' , NULL, 10002, 1 )
insert into #t values ('New' , NULL, 10003, 1 )
insert into #t values ('Active' , NULL, 10004, 1 )
insert into #t values ('Deleted', NULL, 10005, 1 )
insert into #t values ('New' , NULL, 10006, 2 )
insert into #t values ('New' , NULL, 10007, 2 )
insert into #t values ('Deleted', NULL, 10008, 2 )
insert into #t values ('Deleted', NULL, 10009, 2 )
insert into #t values ('Deleted', NULL, 10010, 2 )
insert into #t values ('Deleted', NULL, 10011, 2 )
insert into #t values ('New' , NULL, 10012, 3 )
insert into #t values ('New' , NULL, 10013, 3 )
insert into #t values ('New' , NULL, 10014, 3 )
select num, pgid, case when pgid = 1 then 'Information technology'
when pgid = 2 then 'Human Recource'
else 'Finance' end,count(num)
from #t
where num in (
'New' ,
'Active' ,
'Deleted')
group by num, pgid
SELECT case when pgid = 1 then 'Information technology'
when pgid = 2 then 'Human Recource'
else 'Finance' end,
[New] ,
[Active] ,
[Deleted]
FROM
(SELECT num, pgid
FROM #t
where num in (
'New' ,
'Active' ,
'Deleted')) AS SourceTable
PIVOT
(
count(num)
FOR num IN ([New],
[Active] ,
[Deleted])
) AS PivotTable;
//try this query it will help you to solve this problem
select t.satus,t.PGID,u.nvarchar5,t.total from
(select status,PGID,count(*) as total from USRData group by status,PGID where PGID is not null) t
inner join USRData u on(t.PGID=u.tp_ID) where u.PGID is null and status is not null;