I'd like to apply a WHERE clause to just one field of my select query. The internet told me to use CASE WHEN in the line where I'm selecting my fields and to then remove the where clause. But I was then told that my "selected non aggregate values must be part of the associated group."
The original query looked like this:
SELECT
CAST(EVENT_TIMESTAMP AS DATE) AS Date1,
COUNT(DISTINCT EMAIL) END AS Subs,
SUM(DWELL_MINUTES) AS Dwell
FROM VwNIMEventFct
INNER JOIN VwNIMUserDim ON VwNIMUserDim.NIM_USER_ID = VwNIMEventFct.NIM_USER_ID
INNER JOIN TmpNIMSalesForceDB ON VwNIMUserDim.USER_EMAIL_ADDRESS = EMAIL
WHERE Date1 >= '2013-11-01'
// The problem is here, in the AND clause
AND (SUBSCRIPTION_END_DATE > VwNIMEventFct.EVENT_TIMESTAMP OR SUBSCRIPTION_END_DATE
IS NULL)
GROUP BY Date1
ORDER BY Date1
I then changed the query after doing some searching to this:
SELECT
CAST(EVENT_TIMESTAMP AS DATE) AS Date1,
CASE WHEN (SUBSCRIPTION_END_DATE > Date1 OR SUBSCRIPTION_END_DATE IS NULL)
THEN COUNT(DISTINCT TmpNIMSalesForceDB.EMAIL) END AS Subs,
SUM(VwNIMEventFct.DWELL_MINUTES) AS Dwell
FROM RDMAVWSANDBOX.VwNIMEventFct
INNER JOIN VwNIMUserDim ON VwNIMUserDim.NIM_USER_ID = VwNIMEventFct.NIM_USER_ID
INNER JOIN TmpNIMSalesForceDB ON VwNIMUserDim.USER_EMAIL_ADDRESS = EMAIL
WHERE Date1 >= '2013-11-01'
GROUP BY Date1
ORDER BY Date1
I'd like to select:
1) the date as per "Date1" in the query, then,
2) for each date, the count of distinct email addresses where the SUBSCRIPTION_END_DATE is either NULL or in the future (greater than Date1),
3) Sum of a field (I'm fine here)
How do I do number 2?
EDIT based on answer:
Does this part of the select query ignore and thus not count blank records when
SUBSCRIPTION_END_DATE is null?
SELECT
COUNT(DISTINCT CASE WHEN TmpNIMSalesForceDB.SUBSCRIPTION_END_DATE > Date1 OR TmpNIMSalesForceDB.SUBSCRIPTION_END_DATE IS NULL
THEN TmpNIMSalesForceDB.EMAIL END) AS Subs,
I need to count all records where SUBSCRIPTION_END_DATE is blank/null or where those dates are after Date1.
You need to put your CASE statement inside the COUNT, rather than vice versa, as it needs to be evaluated for each row (which case should this row fall in) and then aggregated across each group (how many rows in that group fell in the non-null group).
COUNT(DISTINCT CASE WHEN (SUBSCRIPTION_END_DATE > Date1 OR SUBSCRIPTION_END_DATE IS NULL)
THEN TmpNIMSalesForceDB.EMAIL END) AS Subs
The COUNT will ignore the NULLs implicitly left by the lack of an ELSE clause in the CASE statement, thus counting only the distinct EMAIL values from rows which met the condition.
Put the case statement inside of the count function.
SELECT
CAST(EVENT_TIMESTAMP AS DATE) AS Date1,
COUNT(DISTINCT(CASE
WHEN SUBSCRIPTION_END_DATE > Date1 OR SUBSCRIPTION_END_DATE IS NULL
THEN TmpNIMSalesForceDB.EMAIL END)) AS Subs,
SUM(VwNIMEventFct.DWELL_MINUTES) AS Dwell
FROM RDMAVWSANDBOX.VwNIMEventFct
INNER JOIN VwNIMUserDim
ON VwNIMUserDim.NIM_USER_ID = VwNIMEventFct.NIM_USER_ID
INNER JOIN TmpNIMSalesForceDB
ON VwNIMUserDim.USER_EMAIL_ADDRESS = EMAIL
WHERE Date1 >= '2013-11-01'
GROUP BY Date1
ORDER BY Date1
Related
I have two tables where I am attempting to join the results into one. I am trying to get the INV_QPC which is the case pack size shown in the results (SEIITN and SKU) are the same product numbers.
The code below gives two results, but the goal is to get the bottom result into the main output, where I was hoping the join would be the lookup to show the case pack size in relation to SKU.
INV_QPC = case pack size
SKU = SKU/Product Number
SEIITN = SKU/Product Number
Thanks for looking.
SELECT
ORDER_QTY, SKU, INVOICE_NUMBER, CUSTOMER_NUMBER, ROUTE,
ALLOCATED_QTY, SHORTED_QTY, PRODUCTION_DATE,
DATEPART(wk, PRODUCTION_DATE) AS FISCAL_WEEK,
YEAR(PRODUCTION_DATE) AS FISCAL_YEAR,
CONCAT(SKU, CUSTOMER_NUMBER) AS SKU_STORE_WEEK
FROM
[database].[dbo].[ORDERS]
WHERE
[PRODUCTION_DATE] >= DATEADD(day, -3, GETDATE())
AND [PRODUCTION_DATE] <= GETDATE()
SELECT INV_QPC
FROM [database].[dbo].[PRODUCT_MASTER]
JOIN [database].[dbo].[ORDERS] ON ORDERS.SKU = PRODUCT_MASTER.SEIITN;
It looks like you are on the right track, but your second SQL statement is only returning the INV_QPC column, so it is not being joined to the first query. Here is an updated SQL statement that should give you the result you are looking for:
SELECT
ORD.ORDER_QTY, ORD.SKU, ORD.INVOICE_NUMBER, ORD.CUSTOMER_NUMBER, ORD.ROUTE,
ORD.ALLOCATED_QTY, ORD.SHORTED_QTY, ORD.PRODUCTION_DATE,
DATEPART(wk, ORD.PRODUCTION_DATE) AS FISCAL_WEEK,
YEAR(ORD.PRODUCTION_DATE) AS FISCAL_YEAR,
CONCAT(ORD.SKU, ORD.CUSTOMER_NUMBER) AS SKU_STORE_WEEK,
PROD.INV_QPC
FROM
[database].[dbo].[ORDERS] ORD
JOIN [database].[dbo].[PRODUCT_MASTER] PROD ON ORD.SKU = PROD.SEIITN
WHERE
ORD.PRODUCTION_DATE >= DATEADD(day, -3, GETDATE())
AND ORD.PRODUCTION_DATE <= GETDATE()
In this query, I have added the INV_QPC column to the SELECT statement, and also included the join condition in the JOIN clause. Additionally, I have given aliases to the tables in the FROM and JOIN clauses to make the query easier to read. Finally, I have updated the WHERE clause to reference the ORD alias instead of the table name directly.
I am writing a SQL query using with as expression. I always get a result in the square of what I required.
This is my query:
DECLARE #MAX_DATE AS INT
SET #MAX_DATE = (SELECT DATEPART(MONTH,FECHA) FROM ALBVENTACAB WHERE NUMALBARAN IN (SELECT DISTINCT MAX(NUMALBARAN) FROM ALBVENTACAB));
;WITH TABLE_LAST AS (
SELECT CONCAT(DATEPART(MONTH,FECHA),'-',DATEPART(YEAR,FECHA)) as LAST_YEAR_MONTH
,SUM(TOTALNETO) AS LAST_YEAR_VALUE
FROM ALBVENTACAB
WHERE DATEPART(YEAR,CURRENT_TIMESTAMP) -1 = DATEPART(YEAR,FECHA) AND NUMSERIE LIKE 'A%'
AND DATEPART(MONTH,FECHA) <= #MAX_DATE
GROUP BY CONCAT(DATEPART(MONTH,FECHA),'-',DATEPART(YEAR,FECHA))
)
,TABLE_CURRENT AS(
SELECT CONCAT(DATEPART(MONTH,FECHA),'-',DATEPART(YEAR,FECHA)) as CURR_YEAR_MONTH
,SUM(TOTALNETO) AS CURR_YEAR_VALUE
FROM ALBVENTACAB
WHERE DATEPART(YEAR,CURRENT_TIMESTAMP) <= DATEPART(YEAR,FECHA) AND NUMSERIE LIKE 'A%'
GROUP BY CONCAT(DATEPART(MONTH,FECHA),'-',DATEPART(YEAR,FECHA))
)
SELECT *
FROM TABLE_CURRENT, TABLE_LAST
When I run the query I get exactly the square of the result.
I want to compare sale monthly with last year.
2-2020 814053.3 2-2019 840295.1
1-2020 1094993.65 2-2019 840295.1
3-2020 293927.3 2-2019 840295.1
2-2020 814053.3 1-2019 1050701.68
1-2020 1094993.65 1-2019 1050701.68
3-2020 293927.3 1-2019 1050701.68
2-2020 814053.3 3-2019 887776.1
1-2020 1094993.65 3-2019 887776.1
3-2020 293927.3 3-2019 887776.1
I should get only 3 rows instead of 9 rows.
You need to properly join your two CTE - the way you're doing it now, you're getting a Cartesian product of each row in either CTE together.
Do something like:
*;WITH TABLE_LAST AS
( ....
),
TABLE_CURRENT AS
( ....
)
SELECT *
FROM TABLE_CURRENT curr
INNER JOIN TABLE_LAST last ON (some join condition here)
What that join condition is going to be - I have no idea, and cannot tell from your question - but you have to define how these two sets of data "connect" ....
It could be something like:
SELECT *
FROM TABLE_CURRENT curr
INNER JOIN TABLE_LAST last ON curr.CURR_YEAR_MONTH = last.LAST_YEAR_MONT
or whatever else makes sense in your situation - but basically, you need to somehow "tie together" these two sets of data and get only those rows that make sense - not just every row from "last" combined with every row from "curr" ....
While you already got the answer on how to join the two results, I thought I'd tell you how to typically approach such problems.
From the same table, you want two sums on different conditions (different years that is). You solve this with conditional aggregation, which does just that: aggregate (sum) based on a condition (year).
select
datepart(month, fecha) as month,
sum(case when datepart(year, fecha) = datepart(year, getdate()) then totalneto end) as this_year,
sum(case when datepart(year, fecha) = datepart(year, getdate()) -1 then totalneto end) as last_year
from albventacab
where numserie like 'A%'
and fecha > dateadd(year, -2, getdate())
group by datepart(month, fecha)
order by datepart(month, fecha);
I have two tables, INVOICES and INV_PRICES. I am trying to find the Invoice table's part price from the Inv_Prices based upon the Invoice_Dt on the Invoice table; if the Invoice_Dt is between (greater than, but less than) or greater than the max EFF_DT on the Inv_Prices, then return that part's price.
I have tired variations on the following code, but no luck. I either do not get all the parts or multiple records.
SELECT DISTINCT A.INVOICE_NBR, A.INVOICE_DT, A.PART_NO,
CASE WHEN TRUNC(A.INVOICE_DT) >= TRUNC(B.EFF_DT) THEN B.DLR_NET_PRC_AM
WHEN (TRUNC(A.INVOICE_DT)||ROWNUM >= TRUNC(B.EFF_DT)||ROWNUM) AND (TRUNC(B.EFF_DT)||ROWNUM <= TRUNC(A.INVOICE_DT)||ROWNUM) THEN B.DLR_NET_PRC_AM
/*MAX(B.EFF_DT) THEN B.DLR_NET_PRC_AM*/
ELSE 0
END AS PRICE
FROM INVOICES A,
INV_PRICES B
WHERE A.PART_NO = B.PART_NO
ORDER BY A.INVOICE_NBR
Can someone assist? I have a sample of each table if needed.
Doesn't it work to put the condition in the JOIN conditions? You can calculate the period when a price is valid using LEAD():
SELECT i.INVOICE_NBR, i.INVOICE_DT, i.PART_NO,
COALESCE(ip.DLR_NET_PRC_AM, 0) as price
FROM INVOICES i LEFT JOIN
(SELECT ip.*, LEAD(eff_dt) OVER (PARTITION BY PART_NO ORDER BY eff_dt) as next_eff_dt
FROM INV_PRICES ip
) ip
ON i.PART_NO = ip.PART_NO AND
i.invoice_dt >= ip.eff_dt AND
(i.invoice_dt < ip.next_eff_dt or ip.next_eff_dt is null)
ORDER BY i.INVOICE_NBR
I am trying to find patients that have more than 1 appointment on the same day. I want to then display all the appointments the patient may have. Do I need to use a subquery to do this? Here is what I have so far:
Select
Appt.ID-PatNm as Patient,
ApptNum,
Sched_ApptType.Prov.Mnemonic as Type,
Appt.Provider-Name as Provider,
Appt.Dt,
Appt.Tm,
Appt.Department-Mnemonic As Dept,
Appt.SchedulerInits,
Case $EXTRACT(Appt.InternalStatus,1)
when 'P' then 'Pending'
when 'A' then 'Arrived'
when 'R' then 'Rescheduled'
End as Status
From Sched.Appointment Appt
JOIN Sched_ApptType.Prov ON
Appt.Department = Sched_ApptType.Prov.Department
and
Appt.Provider = Sched_ApptType.Prov.Provider
and
Appt.Type = Sched_ApptType.Prov.ApptType
Where (Appt.Dt) > DATEADD('DD',-120,CURRENT_DATE)
AND Appt.InternalStatus IN ('P','R','A')
AND Appt.Department-Mnemonic= 'EYE'
Group By
Appt.ID-PatNm,
Appt.Dt
You get the patients having more than one appointment in a day by grouping by patient and day:
select distinct a.id_patnm
from sched.appointment a
group by a.id_patnm, a.dt
having count(*) > 1
So yes, you need a subquery:
Where (Appt.Dt) > DATEADD('DD',-120,CURRENT_DATE)
AND Appt.InternalStatus IN ('P','R','A')
AND Appt.Department_Mnemonic= 'EYE'
AND Appt.ID_PatNm IN
(
select a.id_patnm
from sched.appointment a
group by a.id_patnm, a.dt
having count(*) > 1
)
(BTW: I used id_patnm instead of id-patnm here, for I don't know any DBMS that would allow the hyphen. When using a hyphen in a column name you have to use quotes on the name, e.g. "id-patnm".)
I suppose you could add a column for Appointment_id which would then allow you to get the desired result.
I need to get an active count of patients who have been discharged in a given time frame, but here is the trick. In this table patients can show up more than once, our system uses an episode based system .
Like in (picture 1).
select
p.patient_id,
p.episode_id,
p.case_status,
p.case_substatus,
p.episode_close_date
from patient p
I need no patients to show up in both of these query's.
select * from patient p
where p.case_status = 'a'
-
select * from patient p
where (p.episode_close_date between '2013-01-01 00:00:00.000' and '2013-06-01 00:00:00.000') and p.case_status = 'i'
I guess, what's the best way to do it, would be that that the highest p.episode_id = 'I'. Any ideas on how to do this?
Thanks in advance.
You can do this with aggregation and a having clause. The having clause counts the number of rows that match each condition -- and you want to set the values to 0 because you want both to return no rows:
select patient_id
from patient p
group by patient_id
having sum(case when p.case_status = 'a' then 1 else 0 end) = 0 and
sum(case when (p.episode_close_date between '2013-01-01 00:00:00.000' and '2013-06-01 00:00:00.000') and
p.case_status = 'i'
then 1 else 0
end) = 0;
Basically, the logic in your first two queries in moved into the separate clauses of the having, to count the rows that match each condition.
EDIT:
Here is how you can see the last episode for each patient:
select p.*
from (select p.*,
max(episode_id) over (partition by patient_id) as maxei
from patients p
) p
where episode_id = maxei;
You may also be able to use this with your logic, but I'm not sure about the interplay between the statuses and the dates in the query.