How to achieve this output? - sql

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.

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

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;

Issue with Row_Number() Over Partition

I've been trying to reset the row_number when the value changes on Column Value and I have no idea on how should i do this.
This is my SQL snippet:
WITH Sch(SubjectID, VisitID, Scheduled,Actual,UserId,RLev,SubjectTransactionID,SubjectTransactionTypeID,TransactionDateUTC,MissedVisit,FieldId,Value) as
(
select
svs.*,
CASE WHEN stdp.FieldID = 'FrequencyRegime' and svs.SubjectTransactionTypeID in (2,3) THEN
stdp.FieldID
WHEN stdp.FieldID is NULL and svs.SubjectTransactionTypeID = 1
THEN NULL
WHEN stdp.FieldID is NULL
THEN 'FrequencyRegime'
ELSE stdp.FieldID
END AS [FieldID],
CASE WHEN stdp.Value is NULL and svs.SubjectTransactionTypeID = 1
THEN NULL
WHEN stdp.Value IS NULL THEN
(SELECT TOP 1 stdp.Value from SubjectTransaction st
JOIN SubjectTransactionDataPoint STDP on stdp.SubjectTransactionID = st.SubjectTransactionID and stdp.FieldID = 'FrequencyRegime'
where st.SubjectID = svs.SubjectID
order by st.ServerDateST desc)
ELSE stdp.Value END AS [Value]
from SubjectVisitSchedule svs
left join SubjectTransactionDataPoint stdp on svs.SubjectTransactionID = stdp.SubjectTransactionID and stdp.FieldID = 'FrequencyRegime'
)
select
Sch.*,
CASE WHEN sch.Value is not NULL THEN
ROW_NUMBER() over(partition by Sch.Value, Sch.SubjectID order by Sch.SubjectID, Sch.VisitID)
ELSE NULL
END as [FrequencyCounter],
CASE WHEN Sch.Value = 1 THEN 1--v.Quantity
WHEN Sch.Value = 2 and (ROW_NUMBER() over(partition by Sch.Value, Sch.SubjectID order by Sch.SubjectID, Sch.VisitID) % 2) <> 0
THEN 0
WHEN Sch.Value = 2 and (ROW_NUMBER() over(partition by Sch.Value, Sch.SubjectID order by Sch.SubjectID, Sch.VisitID) % 2) = 0
THEN 1
ELSE NULL
END AS [DispenseQuantity]
from Sch
--left join VisitDrugAssignment v on v.VisitID = Sch.VisitID
where SubjectID = '4E80718E-D0D8-4250-B5CF-02B7A259CAC4'
order by SubjectID, VisitID
This is my Dataset:
Based on the Dataset, I am trying to reset the FrequencyCounter to 1 every time the value changes for each subject, Right now it does 50% of what I want, It is counting when the value 1 or 2 is found, but when value 1 comes again after value 2 it continues the count from where it left. I want every time the value is changes the count to also start from the beginning.
It's difficult to reproduce and test without sample data, but if you want to know how to number rows based on change in column value, next approach may help. It's probably not the best one, but at least will give you a good start. Of course, I hope I understand your question correctly.
Data:
CREATE TABLE #Data (
[Id] int,
[Subject] varchar(3),
[Value] int
)
INSERT INTO #Data
([Id], [Subject], [Value])
VALUES
(1, '801', 1),
(2, '801', 2),
(3, '801', 2),
(4, '801', 2),
(5, '801', 1),
(6, '801', 2),
(7, '801', 2),
(8, '801', 2)
Statement:
;WITH ChangesCTE AS (
SELECT
*,
CASE
WHEN LAG([Value]) OVER (PARTITION BY [Subject] ORDER BY [Id]) <> [Value] THEN 1
ELSE 0
END AS [Change]
FROM #Data
), GroupsCTE AS (
SELECT
*,
SUM([Change]) OVER (PARTITION BY [Subject] ORDER BY [Id]) AS [GroupID]
FROM ChangesCTE
)
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY [GroupID] ORDER BY [Id]) AS Rn
FROM GroupsCTE
Result:
--------------------------------------
Id Subject Value Change GroupID Rn
--------------------------------------
1 801 1 0 0 1
2 801 2 1 1 1
3 801 2 0 1 2
4 801 2 0 1 3
5 801 1 1 2 1
6 801 2 1 3 1
7 801 2 0 3 2
8 801 2 0 3 3
As per my understanding, you need DENSE_RANK as you are looking for the row number will only change when value changed. The syntax will be as below-
WITH your_table(your_column)
AS
(
SELECT 2 UNION ALL
SELECT 10 UNION ALL
SELECT 2 UNION ALL
SELECT 11
)
SELECT *,DENSE_RANK() OVER (ORDER BY your_column)
FROM your_table

SQL Server 2008 count

I have created a simple database to store test results, but I am struggling with the SQL Count to total up my pass/fail/warning to present this.
The idea is that a test run (TRID) will have severaltest sets (TSID), each with several test cases (TCID).
Total should equal amount of test cases for that TSID
Pass should equal amount of test cases with all steps of StatusID=1
Fail should equal amount of test cases with 1 or more step of StatusID=2.
Warning should equal amount of test cases with 1 or more step of StatusID=3, but the same test cases should have zero fails in it. If there is a failed step, then test case should fail as per above.
SQL to create a simplified example of my Results table:-
create table Results (StatusID int, TRID int, TSID int, TCID int);
--Test Set 1 / Test Case 1.
INSERT INTO Results VALUES (1, 1, 1, 1)
INSERT INTO Results VALUES (1, 1, 1, 1)
INSERT INTO Results VALUES (1, 1, 1, 1)
--Test Set 1 / Test Case 2
INSERT INTO Results VALUES (1, 1, 1, 2)
INSERT INTO Results VALUES (1, 1, 1, 2)
--Test Set 2 / Test Case 1
INSERT INTO Results VALUES (1, 1, 2, 1)
INSERT INTO Results VALUES (1, 1, 2, 1)
INSERT INTO Results VALUES (1, 1, 2, 1)
--Test Set 2 / Test Case 2
INSERT INTO Results VALUES (1, 1, 2, 2)
INSERT INTO Results VALUES (2, 1, 2, 2)
--Test Set 3 / Test Case 1
INSERT INTO Results VALUES (1, 1, 3, 1)
INSERT INTO Results VALUES (1, 1, 3, 1)
INSERT INTO Results VALUES (1, 1, 3, 1)
--Test Set 3 / Test Case 2
INSERT INTO Results VALUES (1, 1, 3, 2)
INSERT INTO Results VALUES (3, 1, 3, 2)
--Test Set 4 / Test Case 1
INSERT INTO Results VALUES (1, 1, 4, 1)
INSERT INTO Results VALUES (1, 1, 4, 1)
INSERT INTO Results VALUES (1, 1, 4, 1)
--Test Set 4 / Test Case 2
INSERT INTO Results VALUES (3, 1, 4, 2)
INSERT INTO Results VALUES (2, 1, 4, 2)
SELECT * FROM Results
My current SQL (which you will see provides me with the wrong warning count:-
DECLARE #trid INT
SET #trid = 1
SELECT TRID, TSID,
(SELECT COUNT(DISTINCT TCID) FROM Results WHERE R.TRID = #trID AND R.TSID = TSID) As Total,
(SELECT COUNT(DISTINCT TCID) FROM Results WHERE TRID = #trID AND R.TSID = TSID) - (SELECT COUNT(DISTINCT TCID) FROM Results WHERE TRID = #trID AND R.TSID = TSID AND StatusID = 2) - (SELECT COUNT(DISTINCT TCID) FROM Results WHERE TRID = #trID AND R.TSID = TSID AND StatusID = 3 AND (SELECT COUNT(DISTINCT TCID) FROM Results WHERE TRID = #trID AND R.TSID = TSID AND StatusID = 2) = 0) AS Pass,
(SELECT COUNT(DISTINCT TCID) FROM Results WHERE R.TSID = TSID AND StatusID=2) As Fail,
(SELECT COUNT(DISTINCT TCID) FROM Results WHERE R.TSID = TSID AND StatusID=3) As Warning
FROM Results R
WHERE TRID = #TRID
GROUP BY TRID, TSID
From the above SQL, the current incorrect results are:-
TRID TSID Total Pass Fail Warning
1 1 2 2 0 0
1 2 2 1 1 0
1 3 2 1 0 1
1 4 2 1 1 1*
Results should be....
TRID TSID Total Pass Fail Warning
1 1 2 2 0 0
1 2 2 1 1 0
1 3 2 1 0 1
1 4 2 1 1 0*
Thanks
You could calculate the statistics per test case (TCID) in a subquery. The outer query could then calculate the statistics per test set (TSID). For example:
select TRID
, TSID
, count(*) as Total
, sum(case when FailSteps = 0 and WarnSteps = 0 then 1 else 0 end) as Pass
, sum(case when FailSteps > 0 then 1 else 0 end) as Fail
, sum(case when FailSteps = 0 and WarnSteps > 0 then 1 else 0 end) as Warning
from (
select TRID
, TSID
, TCID
, sum(case when StatusID = 1 then 1 else 0 end) as PassSteps
, sum(case when StatusID = 2 then 1 else 0 end) as FailSteps
, sum(case when StatusID = 3 then 1 else 0 end) as WarnSteps
from Results
group by
TRID
, TSID
, TCID
) as TestCases
group by
TRID
, TSID
Live example at SQL Fiddle.

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

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