Count and GROUP values from a CASE expression - sql

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;

Related

Adding a dummy identifier to data that varies by position and value

I am working on a project in SQL Server with diagnosis codes and a patient can have up to 4 codes but not necessarily more than 1 and a patient cannot repeat a code more than once. However, codes can occur in any order. My goal is to be able to count how many times a Diagnosis code appears in total, as well as how often it appears in a set position.
My data currently resembles the following:
PtKey
Order #
Order Date
Diagnosis1
Diagnosis2
Diagnosis3
Diagnosis 4
345
1527
7/12/20
J44.9
R26.2
NULL
NULL
367
1679
7/12/20
R26.2
H27.2
G47.34
NULL
325
1700
7/12/20
G47.34
NULL
NULL
NULL
327
1710
7/12/20
I26.2
J44.9
G47.34
NULL
I would think the best approach would be to create a dummy column here that would match up the diagnosis by position. For example, Diagnosis 1 with A, and Diagnosis 2 with B, etc.
My current plan is to rollup the diagnosis using an unpivot:
UNPIVOT ( Diag for ColumnALL IN (Diagnosis1, Diagnosis2, Diagnosis3, Diagnosis4)) as unpvt
However, this still doesn’t provide a way to count the diagnoses by position on a sales order.
I want it to look like this:
Diagnosis
Total Count
Diag1 Count
Diag2 Count
Diag3 Count
Diag4 Count
J44.9
2
1
1
0
0
R26.2
1
1
0
0
0
H27.2
1
0
1
0
0
I26.2
1
1
0
0
0
G47.34
3
1
0
2
0
You can unpivot using apply and aggregate:
select v.diagnosis, count(*) as cnt,
sum(case when pos = 1 then 1 else 0 end) as pos_1,
sum(case when pos = 2 then 1 else 0 end) as pos_2,
sum(case when pos = 3 then 1 else 0 end) as pos_3,
sum(case when pos = 4 then 1 else 0 end) as pos_4
from data d cross apply
(values (diagnosis1, 1),
(diagnosis2, 2),
(diagnosis3, 3),
(diagnosis4, 4)
) v(diagnosis, pos)
where diagnosis is not null;
Another way is to use UNPIVOT to transform the columns into groupable entities:
SELECT Diagnosis, [Total Count] = COUNT(*),
[Diag1 Count] = SUM(CASE WHEN DiagGroup = N'Diagnosis1' THEN 1 ELSE 0 END),
[Diag2 Count] = SUM(CASE WHEN DiagGroup = N'Diagnosis2' THEN 1 ELSE 0 END),
[Diag3 Count] = SUM(CASE WHEN DiagGroup = N'Diagnosis3' THEN 1 ELSE 0 END),
[Diag4 Count] = SUM(CASE WHEN DiagGroup = N'Diagnosis4' THEN 1 ELSE 0 END)
FROM
(
SELECT * FROM #x UNPIVOT (Diagnosis FOR DiagGroup IN
([Diagnosis1],[Diagnosis2],[Diagnosis3],[Diagnosis4])) up
) AS x GROUP BY Diagnosis;
Example db<>fiddle
You can also manually unpivot via UNION before doing the conditional aggregation:
SELECT Diagnosis, COUNT(*) As Total Count
, SUM(CASE WHEN Position = 1 THEN 1 ELSE 0 END) As [Diag1 Count]
, SUM(CASE WHEN Position = 2 THEN 1 ELSE 0 END) As [Diag2 Count]
, SUM(CASE WHEN Position = 3 THEN 1 ELSE 0 END) As [Diag3 Count]
, SUM(CASE WHEN Position = 4 THEN 1 ELSE 0 END) As [Diag4 Count]
FROM
(
SELECT PtKey, Diagnosis1 As Diagnosis, 1 As Position
FROM [MyTable]
UNION ALL
SELECT PtKey, Diagnosis2 As Diagnosis, 2 As Position
FROM [MyTable]
WHERE Diagnosis2 IS NOT NULL
UNION ALL
SELECT PtKey, Diagnosis3 As Diagnosis, 3 As Position
FROM [MyTable]
WHERE Diagnosis3 IS NOT NULL
UNION ALL
SELECT PtKey, Diagnosis4 As Diagnosis, 4 As Position
FROM [MyTable]
WHERE Diagnosis4 IS NOT NULL
) d
GROUP BY Diagnosis
Borrowing Aaron's fiddle, to avoid needing to rebuild the schema from scratch, and we get this:
https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=d1f7f525e175f0f066dd1749c49cc46d

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

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.

SQL select where records match in two fields

TABLE:A
acctno, ratcod, ssno
select only records where ssno is the same and ratcod contains any of 6,7,8 and the same ssno is found elsewhere to contain ratcod value <> 6,7,8
DATA TABLE A
acctno ratcod ssno
123456 6 123-44-5566
23456 4 123-44-5566
12345 1 123-44-5566
654321 6 333-44-5566
664433 6 222-44-5566
222336 2 123-44-5566
554646 4 444-98-9876
DESIRED OUTPUT
acctno ratcod ssno
123456 6 123-44-5566
23456 4 123-44-5566
12345 1 123-44-5566
222336 2 123-44-5566
Trying the following produces an error
DB2 for i5/OS]SQL0199 - Keyword IN not expected.
select tableA.*
from (select ssno from refdat1.lnmast lnmast
group by lnmast.ssno
having sum(case ratcod in (6, 7 ,8) then 1 else 0 end) > 0
and sum(case ratcod in (6, 7, 8) then 0 else 1 end) > 0)
as ASum
JOIN tableA
ON tableA.ssno = ASum.ssno
The logic is: find all ssnos having a ratcod in (6, 7, 8) and having a ratcod not in (6, 7, 8). Once these are found, return all the rows with that ssno.
select tableA.*
from (select ssno
from tableA
group by ssno
having sum(case ratcod in (6, 7, 8) then 1 else 0 end) > 0
and sum(case ratcod in (6, 7, 8) then 0 else 1 end) > 0)
as ASum
JOIN TableA
ON TableA.ssno = ASum.ssno

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.