Count distinct using case when - mysql

Let me first start by saying I am new to mysql. I am transitioning from dumping data into excel and using voodoo from there so this new language can be perplexing but I am getting the hang of simple queries.
My first problem that I cannot find an answer for is that I am looking for billable medical encounters and then comparing them to how many patients were seen for that time period. (Edit: It is the "CASE WHEN statement that I am having issue with here)
Billable encounters are counted by unique encounters that can have one of any number of billable codes for that encounter to count as billable. (There are many more codes but I truncated for space)
This is what I have this far which of course is not working:
SELECT
CONCAT(`users`.`ulname`,', ',`users`.`ufname`) AS Provider
, YEAR(e.`date`) AS 'YEAR'
, LEFT(MONTHNAME(e.date), 3) AS 'MONTH'
, COUNT(DISTINCT e.`patientID`) AS Pts
, COUNT(DISTINCT e.`encounterID` CASE WHEN d.`value`
IN ('99213','99214','99212','99203','99202','90832','99393') THEN END AS
Bill_Encs
, COUNT(e.`status`) AS 'Vts_CHK'
, COUNT(e.`status`) - COUNT(DISTINCT e.`encounterID`)
AS No_Codes
FROM enc e
INNER JOIN `mobiledoc`.`users` ON `users`.`uid` = e.`ResourceId`
INNER JOIN `billingdata` billing ON billing.`EncounterId` =
e.`encounterID`
INNER JOIN items i ON i.`itemID` = billing.`itemID`
INNER JOIN `itemdetail` d ON d.`itemID` = i.`itemID`
WHERE e.`date` BETWEEN CAST('2017-10-26' AS DATE) AND CAST('2017-10-31' AS
DATE)
AND d.`propID` = 13
AND billing.`deleteFlag` = 0
AND i.`deleteFlag` = 0
AND e.status = "CHK"
AND e.`deleteFlag` = 0
AND e.`encType` = 1
AND e.`ClaimReq` = 1
AND `users`.`UserType` = 1
GROUP BY Provider ASC, YEAR(e.`date`) ,MONTH(e.`date`) ASC;

If all else is equal (working) I think you just want conditional aggregation.
, COUNT(DISTINCT e.`encounterID` CASE WHEN d.`value`
IN ('99213','99214','99212','99203','99202','90832','99393') THEN END AS
Bill_Encs
becomes
, COUNT(DISTINCT CASE WHEN d.`value`
IN ('99213','99214','99212','99203','99202','90832','99393') THEN e.`encounterID` END AS Bill_Encs

Related

How can I correct this query that involves a CASE statement for a summary?

I'm currently trying to solve an issue revolving around summarizing a list of publishers, their total revenue, total payouts, and their net profit. What makes this slightly complicated is that the total payout is contingent on a case statement (due to having to choose between the higher value of royalties). This case statement was perfectly fine and executed in a previous query that you can see on the SQLFiddle link down below. My issue is that I have a near finished query that addresses what I need but I don't know what correction to make for it to complete. Help would be super appreciated! And if you get it to work, you would be a legit lifesaver!!
Select name,
SUM(book.msrp) AS 'Total Revenue',
SUM(EarningByBook) AS 'Total Payouts',
SUM(book.msrp)-SUM(EarningByBook) AS 'Net Profit'
FROM
(SELECT publisher.name, book.msrp,
(SELECT
CASE WHEN preferred_royalties > standard_royalties
THEN preferred_royalties*copies_sold
ELSE standard_royalties*copies_sold END
AS 'EarningByBook',
copies_sold ,
YEAR(CURDATE())-YEAR(date_published) Years
INNER JOIN book ON publisher.id = book.publisher_id)
FROM author A
JOIN book B ON A.id=B.author_id
JOIN publisher P ON B.publisher_id=P.id)
From publisher
INNER JOIN book ON publisher.id = book.publisher_id) Z
GROUP BY
name;
The SQL fiddle is as follows :
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=b0015a0a4286f9b2c064bbd65525faa5&hide=13312
The output expected should look
Publisher
Total Revenue
Total Payouts
Net Profit
name
20000
1500
18500
name
15000
1000
14000
Consider flattening all your inner selects to a single SELECT subquery.
SELECT sub.publisher
, SUM(sub.msrp) AS 'Total Revenue'
, SUM(sub.EarningByBook) AS 'Total Payouts'
, SUM(sub.msrp) - SUM(sub.EarningByBook) AS 'Net Profit'
FROM
(SELECT
P.`name` AS publisher
, CASE
WHEN A.preferred_royalties > P.standard_royalties
THEN A.preferred_royalties * B.copies_sold
ELSE P.standard_royalties * B.copies_sold
END AS EarningByBook
, YEAR(CURDATE()) - YEAR(B.date_published) AS Years
, B.msrp
, B.copies_sold
FROM author A
INNER JOIN book B
ON A.id = B.author_id
INNER JOIN publisher P
ON B.publisher_id = P.id
) AS sub
GROUP BY
sub.publisher;

How to exclude rows from a SQL query with an "IF"-like condition

I have a feeling that this answer is staring at me right in the face...I just can't see it. I've also ran into this problem many times, but I can't seem to wrap my head around a solid answer for it. I need to exclude some rows from my SQL query with just a simple condition. Here is my code:
IF OBJECT_ID('tempdb..#NextEvent') IS NOT NULL DROP TABLE #NextEvent
CREATE TABLE #NextEvent(
CaseDisplayNumber VARCHAR(20)
, ScheduledDay DATETIME
, EventTypeID INT
, EventType VARCHAR(50)
, EventResult VARCHAR(50)
, MatterTypeID INT
, RowNum INT
)
INSERT INTO #NextEvent
SELECT CC.CaseDisplayNumber
,SD.ScheduledDay
,SE.EventTypeID EventTypeID
,ETC.EventType EventType
,ERC.EventResult
,MTC.MatterTypeID
,ROW_NUMBER() OVER(PARTITION BY CC.CaseDisplayNumber ORDER BY SD.ScheduledDay DESC)
FROM CourtCase CC
LEFT JOIN calendar.CaseAssociateEvent CCAE ON CCAE.CourtCaseID = CC.CourtCaseID
LEFT JOIN calendar.CaseEventHeader CEH ON CEH.CaseEventHeaderID = CCAE.CaseEventHeaderID
LEFT JOIN calendar.ScheduledDay SD ON SD.CaseEventHeaderID = CEH.CaseEventHeaderID
LEFT JOIN calendar.ScheduledEvent SE ON SE.CaseEventHeaderID = CEH.CaseEventHeaderID
LEFT JOIN calendar.ScheduledResult SR ON SR.ScheduledEventID = SE.ScheduledEventID
LEFT JOIN calendar.EventResultCodes ERC ON ERC.EventResultID = SR.EventResultID
LEFT JOIN calendar.MatterTypeCodes MTC ON MTC.MatterTypeID = CEH.MatterTypeID
LEFT JOIN calendar.EventTypeCodes ETC ON ETC.EventTypeID = SE.EventTypeID
ORDER BY CC.CaseDisplayNumber
OPTION (MAXDOP 2)
IF OBJECT_ID('tempdb..#ChargeDispo') IS NOT NULL DROP TABLE #ChargeDispo
CREATE TABLE #ChargeDispo(
CaseDisplayNumber VARCHAR(20)
, Charge VARCHAR(20)
, ChargeClass VARCHAR(5)
, DispositionCD VARCHAR(2)
)
INSERT INTO #ChargeDispo
SELECT DISTINCT CC.CaseDisplayNumber
,CCD.ARSCode Charge
,CCC.ChargeClass ChargeClass
,DC.DispositionCD Disposition
FROM CourtCase CC
JOIN CaseAction CA ON CA.CourtCaseID = CC.CourtCaseID
JOIN PartyCaseActionRole PCAR ON PCAR.CaseActionID = CA.CaseActionID
JOIN Charge C ON C.PartyCaseActionRoleID = PCAR.PartyCaseActionRoleID
JOIN ChargeActivity CAY ON CAY.ChargeID = C.ChargeID AND CAY.ChargeStatusID = 1
JOIN ChargeCodes CCD ON CCD.ChargeCodeID = CAY.ChargeCodeID
JOIN ChargeClassCodes CCC ON CCC.ChargeClassID = CCD.ChargeClassID
LEFT JOIN ChargeDisposition CD ON CD.ChargeActivityID = CAY.ChargeActivityID
LEFT JOIN DispositionStatusCodes DSC ON DSC.DispositionStatusID = CD.DispositionStatusID
LEFT JOIN DispositionCodes DC ON DC.DispositionID = CD.DispositionID
WHERE DSC.DispositionStatusID = 1
ORDER BY CCC.ChargeClass
OPTION (MAXDOP 2)
SELECT DISTINCT CC.CaseDisplayNumber CaseNumber
,CC.FileDate FileDate
,EN.FullName Defendant
,CD.ChargeClass Class
,CD.DispositionCD DispositionID
,NE.EventType EventType
,NE.ScheduledDay ScheduledDay
,NE.EventResult EventResult
,PD.PayDate DueDate
,fnGetFinancialBalance(FP.financialpartyid) Balance
FROM CourtCase CC
JOIN CaseAction CA ON CA.CourtCaseID = CC.CourtCaseID AND CC.CaseStatusID = 1
JOIN PayDate PD ON PD.CaseActionID = CA.CaseActionID AND PD.EndDate IS NULL
JOIN PartyCaseActionRole PCAR ON PCAR.CaseActionID = CA.CaseActionID AND PCAR.PartyRoleID = 4
JOIN financial.FinancialParty FP ON FP.PartyID = PCAR.PartyID
JOIN Party P ON P.PartyID = PCAR.PartyID
JOIN PartyRoleCodes PRC ON PRC.PartyRoleID = PCAR.PartyRoleID AND PRC.PartyRoleID = 4
JOIN Entity E ON E.EntityID = P.EntityID
JOIN EntityName EN ON EN.EntityID = E.EntityID
JOIN #ChargeDispo CD ON CD.CaseDisplayNumber = CC.CaseDisplayNumber
JOIN #NextEvent NE ON NE.CaseDisplayNumber = CC.CaseDisplayNumber AND NE.RowNum = 1
WHERE
NE.ScheduledDay <= PD.PayDate
AND (CD.ChargeClass = 'CV' OR CD.ChargeClass = 'PK')
AND (CD.DispositionCD = '11' OR CD.DispositionCD = '12' OR CD.DispositionCD = '21' OR CD.DispositionCD = '22')
AND fnGetFinancialBalance(FP.financialpartyid) > 0
ORDER BY PD.PayDate
OPTION (MAXDOP 2)
My trouble is in the last WHERE statement. I'm not sure if it even needs to be there either. My results are almost perfect, I just need to get rid of rows IF the NE.MatterTypeID = 3, AND the NE.ScheduledDay is GREATER than PD.PayDate (basically get rid of all future court dates that are after the pay date). Other than that, I want to show everything else. This could really help me out on future queries as well. I have been looking for similar problems, but can't find an answer that can really help me out. Unless I'm not searching right, which could very possibly be the case. I've tried IF, I've tried OR's, I've tried CASE (which confuses me a little). The line:
NE.ScheduledDay <= PD.PayDate
works, I just need it to be a little more specific.
The first thing you need to do is convert your logic to match the way you refer to it. For example, if you say "x needs to be greater than y", your code should reflect that, not the opposite ("y needs to be <= x"). That just helps with clarity.
Your condition would then be
Where NOT(NE.MatterTypeID = 3 and NE.ScheduledDay > PD.PayDate) AND --...
Second, your issue is likely a date vs datetime problem. If your dates are datetime values, you'll need to lop off the time data by casting to a date. It may be possible for the scheduled day to be 11/5/2014 8:00 PM, while the pay date is 11/5/2014 8:00 am. On the surface, you'd expect them to be the same, but the time value could be throwing you off.
I recommend changing the column type to date if you'll never use the time value. That removes the need to lop off the time in future queries. If you can't do that, you can cast as a date to make the dates time-agnostic:
Where NOT(NE.MatterTypeID = 3 and CAST( NE.ScheduledDay as date) > CAST(PD.PayDate as date) ) AND --...

Incorrect SUM when using two LEFT JOINs and a GROUP BY

The following code returns an incorrect value for the sumHours field. It appears to prepare the sumHours field then once the GROUP BY runs, sum the sums together.
SELECT mmr_ID, mmr_projectName, SUM(mmr_hoursWorked.mmr_hoursWorked_hours) AS sumHours
FROM mmr
LEFT JOIN mmr_hoursWorked
ON mmr.mmr_ID = mmr_hoursWorked.mmr_hoursWorked_project AND mmr_hoursWorked.mmr_hoursWorked_mm = "P90826"
LEFT JOIN mmr_notes
ON mmr.mmr_ID = mmr_notes.mmr_notes_MMR_ref AND mmr_notes.mmr_notes_author = "P90826"
WHERE mmr_mmAssigned = "P90826" AND mmr_projectStatus != 1 OR mmr_notes.mmr_notes_author = "P90826" AND mmr_projectStatus != 1
GROUP BY mmr_ID
Actual Results
mmr_ID - 35
mmr_projectName - Project A
sumHours - 140.2
Expected Results
mmr_ID - 35
mmr_projectName - Project A
sumHours - 35.05
Due to JOIN statements combination of results are returned, so you should handle aggregates and joins separately. Try this:
SELECT t.*
FROM
(
SELECT mmr_ID, mmr_projectName, SUM(mmr_hoursWorked.mmr_hoursWorked_hours) AS sumHours
FROM mmr
LEFT JOIN mmr_hoursWorked
ON mmr.mmr_ID = mmr_hoursWorked.mmr_hoursWorked_project AND mmr_hoursWorked.mmr_hoursWorked_mm = 'P90826'
WHERE mmr_projectStatus != 1 AND mmr_mmAssigned = 'P90826'
GROUP BY mmr_ID, mmr_projectName, mmr_mmAssigned
) t
LEFT JOIN mmr_notes
ON t.mmr_ID = mmr_notes.mmr_notes_MMR_ref
WHERE mmr_notes.mmr_notes_author = 'P90826';
The issue was corrected by normalizing the database. The mmr_notes table was integrated into the mmr_hoursWorked table since it only had one unique field.

MySQL: how can I count number of articles by a join table

I have a table with news items, I have another table with media_types, I want to make one simple query that reads the media_types table and count for each record how many news_items exist.
The result will be turned into a json response that I will use for a chart, this is my SQLstatement
SELECT
gc.country AS "country"
, COUNT(*) AS "online"
FROM default_news_items AS ni
JOIN default_news_item_country AS nic ON (nic.id = ni.country)
JOIN default_country AS c ON (nic.country = c.id)
JOIN default_geo_country AS gc ON (gc.id = c.geo_country)
LEFT JOIN default_medias ON (m.id = ni.media)
WHERE TRUE
AND ni.deleted = 0
AND ni.date_item > '2013-10-23'
AND ni.date_item < '2013-10-29'
AND gc.country <> 'unknown'
AND m.media_type = '14'
GROUP BY gc.country
ORDER BY `online` desc LIMIT 10
This is the json respond I create from the mysql respond
[
{"country":"New Zealand","online":"7"},
{"country":"Switzerland","online":"1"}
]
How do I add print and social data to my output like this
I would like the json respond look like this
[
{"country":"New Zealand","online":"7", "social":"17", "print":"2"},
{"country":"Switzerland","online":"1", "social":"7", "print":"1"}
]
Can I use the count (*) in the select statement to do something like this
COUNT( * ) as online, COUNT( * ) as social, COUNT( * ) as print
Is it possible or do I have to do several SQL statement to get the data I'm looking for?
This is the general structure:
SELECT default_geo_country.country as country,
SUM(default_medias.media_type = 14) as online,
SUM(default_medias.media_type = XX) as social,
SUM(default_medias.media_type = YY) as print
FROM ...
JOIN ...
WHERE ...
GROUP BY country
I think you want conditional aggregation. Your question, however, only shows the online media type.
Your query would be more readable by using table aliases and removing the back quotes. Also, if media_type is an integer, then you should not enclose the constant for comparison in single quotes -- I, for one, find it misleading to compare a string constant to an integer column.
I suspect this is the way you want to go. Where the . . . is, you want to fill in with the counts for the other media types.
SELECT default_geo_country.country as country,
sum(media_type = '14') as online,
sum(default_medias.media_type = XX) as social,
sum(default_medias.media_type = YY) as print
. . .
FROM default_news_items ni JOIN
default_news_item_country nic
ON nic.id = ni.country JOIN
default_country dc
ON nic.country = dc.id JOIN
default_geo_country gc
ON gc.id = dc.geo_country LEFT JOIN
default_medias dm
ON dm.id = dni.media
WHERE ni.deleted = '0'
AND ni.date_item > '2013-10-23'
AND ni.date_item < '2013-10-29'
AND gc.country <> 'unknown'
GROUP BY gc.country
ORDER BY online desc
LIMIT 10

count multiple in one statement

i have a table with data title and date of the data inserted.
and right now i want to do count to make the statistic out of it.
can i do multiple count in one sql statement?
like from, the column date, i want to count how many on this month, and how many in this year, until month selected. is it possible? if yes, how?
this is what i have come up, for now.
SELECT a.trigger_type_code
, c.trigger_name
, COUNT(*) AS number
FROM issue_trigger a
INNER JOIN cs_issue b
ON b.issue_id = a.issue_id
INNER JOIN ref_trigger_type c
ON c.trigger_type_code = a.trigger_type_code
WHERE MONTH(b.created_date) = '05'
AND YEAR(b.created_date) = '2011'
GROUP BY trigger_type_code,trigger_name
by this is only for one count.help.:(
You could use a case:
select sum(case when MONTH(b.created_date) = '05'
AND YEAR(b.created_date) = '2011' then 1 end) as Count_2011_05
, sum(case when YEAR(b.created_date) = '2011'
then 1 end) as Count_2011
from ... etc ...
I think you could go like this:
SELECT
a.trigger_type_code,
c.trigger_name,
COUNT(MONTH(b.created_date) < 5 OR NULL) AS before_the_month,
COUNT(MONTH(b.created_date) = 5 OR NULL) AS in_the_month
FROM issue_trigger a
INNER JOIN cs_issue b
ON b.issue_id = a.issue_id
INNER JOIN ref_trigger_type c
ON c.trigger_type_code = a.trigger_type_code
WHERE YEAR(b.created_date) = 2011
GROUP BY a.trigger_type_code, c.trigger_name