I am trying to run a query that obtains an average for two different variables then calculates the difference between them. However, the I keep getting an error that the query doesn't recognize the variable names in the second calculation. I have been looking around but I feel like I am just missing a simple step here
Here is the full query
select
DATEPART(dw,t1.Date),
DATENAME(dw,t1.Date),
AVG (CASE WHEN (rraw.Mode='Passive') THEN T1.LengthSec + T1.Sec ELSE NULL
END) AS 'Passive',
AVG (CASE WHEN (rraw.Mode='Active') THEN T1.Sec + T1.Sec ELSE NULL
END) AS 'Active'
then ( Passive - Active ) / Passive * 100.0 AS 'ModePrctDiff'
from bm.t1agent t1 with (nolock)
JOIN prc.Request rreq
on t1.id = rreq.t1ID
join PRC.Raw rraw with (nolock)
on rreq.ID = rraw.Id
where t1.Date >= '2014-12-07'
GROUP BY ROLLUP ((DATEPART(dw,t1.Date),DATENAME(dw,t1.Date)))
The error is occurring on the then statement. If I run it without that part, I obtain these results
Day Of Week Passive Active
Sunday 350.54 370.54
Monday 365.54 380.91
I want to have the results read
Day Of Week Passive Active Difference
Sunday 350.54 370.54 xxxx
Monday 365.54 380.91 xxxx
you can't use the alias name given to a column in the same select clause
you need to write it as a subquery or cte.
with cte
as
(
select
DATEPART(dw,t1.Date) as dw1,
DATENAME(dw,t1.Date) as dw2,
AVG (CASE WHEN (rraw.Mode='Passive') THEN T1.LengthSec + T1.Sec ELSE NULL
END) AS 'Passive',
AVG (CASE WHEN (rraw.Mode='Active') THEN T1.Sec + T1.Sec ELSE NULL
END) AS 'Active'
from bm.t1agent t1 with (nolock)
JOIN prc.Request rreq
on t1.id = rreq.t1ID
join PRC.Raw rraw with (nolock)
on rreq.ID = rraw.Id
where t1.Date >= '2014-12-07'
GROUP BY ROLLUP ((DATEPART(dw,t1.Date),DATENAME(dw,t1.Date)))
)
select * , ( Passive - Active ) / Passive * 100.0 AS 'ModePrctDiff'
from cte
Related
I have a table with emails and date. I want to compare 2022's emails against 2021's emails. I got the code down below. Pretty straight forward. But now I want to add 2022's emails to the "bucket" as time passes. So Jan-2022 emails will be compared to 2021's emails, Feb-2022 emails will be compared to 2021's emails + Jan-2022, and so on.
Any helpful advice would be greatly appreciated.
SELECT T1.date
,IFF(T1.email = T2.email,'TRUE','FALSE') "Logic"
,SUM(CASE WHEN "Logic" = 'FALSE' THEN 1 ELSE 0 END) "New email"
,SUM(CASE WHEN "Logic" = 'TRUE' THEN 1 ELSE 0 END) "Repeated email"
FROM T1
LEFT JOIN
(SELECT DISTINCT email
,date
FROM T1
WHERE
AND "date" >= '2021-01-01'
AND "date" <= '2021-12-31') T2
ON T1.email = T2.email
WHERE T1.date >= '2022-01-01'
AND T1.date <= '2022-12-31'
GROUP BY 1,2);
If I understand you, you want to count the number of emails per day; those that were new and those that you had seen before on a prior day.
If this is right, I would take a completely different approach and use the count window/analytic function for each email:
select
email, date,
count (*) over (partition by email order by date) as email_count
from t1
This will tell you for each email how many times total (chronologically) it has occurred, which means a count of 1 means it's the first time and anything else means it's a repeat.
From there, you can just do a normal grouping:
with foo as (
select
email, date,
count (*) over (partition by email order by date) as email_count
from t1
)
select
date,
sum (case when email_count > 1 then 1 else 0 end) as repeat_count,
sum (case when email_count = 1 then 1 else 0 end) as new_count
from foo
group by date
order by date
You can apply a date filter in the final query, provided the CTE is always all data.
It's not clear what you're working with as the SQL statement is incomplete / invalid (mismatching parenthesis), and the data granularity is also unclear - is it daily, or monthly data?
But I think the problem you are having is that you're only determining "new" emails based on whether you saw the email address last year, where you need to determine based on whether you've seen the address at all, e.g. yesterday or any day prior.
To do this you'll want to
avoid filtering the T2 "list of emails we've seen" subquery by date, and
use a conditional date clause when joining T2 back to T1
Like this
SELECT T1.date
,IFF(T1.email = T2.email,'TRUE','FALSE') "Logic"
,SUM(CASE WHEN "Logic" = 'FALSE' THEN 1 ELSE 0 END) "New email"
,SUM(CASE WHEN "Logic" = 'TRUE' THEN 1 ELSE 0 END) "Repeated email"
FROM T1
LEFT JOIN
(SELECT DISTINCT email
,date
FROM T1
WHERE
-- remove these:
-- AND "date" >= '2021-01-01'
-- AND "date" <= '2021-12-31'
) T2
ON T1.email = T2.email
WHERE T1.date >= '2022-01-01'
AND T1.date <= '2022-12-31'
-- add this:
AND T1.date > T2.date
GROUP BY 1,2);
The key here is that the join condition doesn't need to be an equality join; it can be any expression or function that evaluates to a boolean TRUE/FALSE. So for every email and date, you can compare it to every matching email on a previous (historical) date.
I have a query on a transaction table that returns the Summarized total on a column for each ID based on a data range. The query works great except it doesn't include those IDs that don't have data in the transaction table. How can I include those IDs in my result filled with a zero total. Here's a simplified version of my query.
SELECT tblID.IDName
,SUM(CASE
WHEN tblTransactions.idxTransType = 30
THEN CAST(tblTransactions.TimeAmount AS FLOAT) / 60.0
ELSE 0
END) AS 'Vacation'
FROM tblTransactions
INNER JOIN tblTransTypes ON tblTransactions.idxTransType = tblTransTypes.IdxTransType
INNER JOIN tblID ON tblTransactions.idxID = tblID.IdxID
WHERE (tblTransactions.Deleted = 0)
AND (tblTransactions.NotCurrent = 0)
AND (tblTransactions.TransDate >= CONVERT(DATETIME, 'March 1, 2018', 102))
AND (tblTransactions.TransDate <= CONVERT(DATETIME, 'April 11, 2018', 102))
GROUP BY tblID.IDName
Actually it's slightly more complicated than that:
SELECT
i.IDName,
SUM(CASE WHEN t.idxTransType = 30 THEN CAST(t.TimeAmount AS FLOAT) / 60.0 ELSE 0 END) AS 'Vacation'
FROM
tblID i
LEFT JOIN tblTransactions t ON t.idxID = i.IdxID AND t.Deleted = 0 AND t.NotCurrent = 0 AND t.TransDate BETWEEN '20180301' AND '20180411'
LEFT JOIN tblTransTypes tt ON tt.IdxTransType = t.idxTransType
GROUP BY
i.IDName;
You want left joins:
SELECT i.IDName,
SUM(CASE WHEN t.idxTransType = 30 THEN CAST(t.TimeAmount AS Float) / 60.0 ELSE 0 END) AS Vacation
FROM tblID i LEFT JOIN
tblTransactions t
ON t.idxID = i.IdxID AND
t.Deleted = 0 AND
t.NotCurrent = 0 AND
t.TransDate >= '2018-03-01' AND
t.TransDate <= '2018-04-11'
tblTransTypes tt
ON t.idxTransType = tt.IdxTransType
GROUP BY i.IDName;
Notes:
Table aliases make the query much easier to write and to read.
Use ISO/ANSI standard date formats.
The filter conditions on all but the first table belong in the ON clauses.
I want to show the days since the last customer order from a certain storenumber, I have been told to use CASE.
I don't want to use MAX or MIN because it may ignore other records for said customer.
SELECT ms.CustomerID AS email,
AS last_txn_days_online,
CASE
WHEN ST2.StoreNumber != '100799' THEN CEILING(Round(DateDiff(DAY, Min(st2.PurchaseDate), Max(st2.PurchaseDate)) / NULLIF(Count(st2.CustomerID) - 1, 0),0))
ELSE NULL
END AS last_txn_days_instore
FROM [Not MS] ms
LEFT JOIN [ORDER_HEADER] st2 ON ms.CustomerID = st2.CustomerID
GROUP BY MS.CustomerID
I think you want conditional aggregation. The query would look something like this:
SELECT ms.CustomerID AS email,
CEILING(Round(DateDiff(DAY,
Min(CASE WHEN ST2.StoreNumber <> '100799' THEN st2.PurchaseDate END),
M MAX(CASE WHEN ST2.StoreNumber <> '100799' THEN st2.PurchaseDate END)
) / NULLIF(Count(CASE WHEN ST2.StoreNumber <> '100799' THEN st2.CustomerID END) - 1, 0), 0
)
)
END AS last_txn_days_instore
FROM [Not MS] ms LEFT JOIN
[ORDER_HEADER] st2
ON ms.CustomerID = st2.CustomerID
GROUP BY MS.CustomerID
I have the following code which gives me production dates and production volumes for a thirty day period.
select
(case when trunc(so.revised_due_date) <= trunc(sysdate)
then trunc(sysdate) else trunc(so.revised_due_date) end) due_date,
(case
when (case when sp.pr_typ in ('VV','VD') then 'DVD' when sp.pr_typ in ('RD','CD')
then 'CD' end) = 'CD'
and (case when so.tec_criteria in ('PI','MC')
then 'XX' else so.tec_criteria end) = 'OF'
then sum(so.revised_qty_due)
end) CD_OF_VOLUME
from shop_order so
left join scm_prodtyp sp
on so.prodtyp = sp.prodtyp
where so.order_type = 'MD'
and so.plant = 'W'
and so.status_code between '4' and '8'
and trunc(so.revised_due_date) <= trunc(sysdate)+30
group by trunc(so.revised_due_date), so.tec_criteria, sp.pr_typ
order by trunc(so.revised_due_date)
The problem I have is where there is a date with no production planned, the date wont appear on the report. Is there a way of filling in the missing dates.
i.e. the current report shows the following ...
DUE_DATE CD_OF_VOLUME
14/04/2015 35,267.00
15/04/2015 71,744.00
16/04/2015 20,268.00
17/04/2015 35,156.00
18/04/2015 74,395.00
19/04/2015 3,636.00
21/04/2015 5,522.00
22/04/2015 15,502.00
04/05/2015 10,082.00
Note: missing dates (20/04/2015, 23/04/2015 to 03/05/2015)
Range is always for a thirty day period from sysdate.
How do you fill in the missing dates?
Do you need some kind of calendar table?
Thanks
You can get the 30-day period from SYSDATE as follows (I assume you want to include SYSDATE?):
WITH mydates AS (
SELECT TRUNC(SYSDATE) - 1 + LEVEL AS due_date FROM dual
CONNECT BY LEVEL <= 31
)
Then use the above to do a LEFT JOIN with your query (perhaps not a bad idea to put your query in a CTE as well):
WITH mydates AS (
SELECT TRUNC(SYSDATE) - 1 + LEVEL AS due_date FROM dual
CONNECT BY LEVEL <= 31
), myorders AS (
select
(case when trunc(so.revised_due_date) <= trunc(sysdate)
then trunc(sysdate) else trunc(so.revised_due_date) end) due_date,
(case
when (case when sp.pr_typ in ('VV','VD') then 'DVD' when sp.pr_typ in ('RD','CD')
then 'CD' end) = 'CD'
and (case when so.tec_criteria in ('PI','MC')
then 'XX' else so.tec_criteria end) = 'OF'
then sum(so.revised_qty_due)
end) CD_OF_VOLUME
from shop_order so
left join scm_prodtyp sp
on so.prodtyp = sp.prodtyp
where so.order_type = 'MD'
and so.plant = 'W'
and so.status_code between '4' and '8'
and trunc(so.revised_due_date) <= trunc(sysdate)+30
group by trunc(so.revised_due_date), so.tec_criteria, sp.pr_typ
order by trunc(so.revised_due_date)
)
SELECT mydates.due_date, myorders.cd_of_volume
FROM mydates LEFT JOIN myorders
ON mydates.due_date = myorders.due_date;
If you want to show a zero on "missing" dates instead of a NULL, use COALESCE(myorders.cd_of_volume, 0) AS cd_of_volume above.
what you can do is this :
creating a new table with all the days you need .
WITH DAYS AS
(SELECT TRUNC(SYSDATE) - ROWNUM DDD
FROM ALL_OBJECTS
WHERE ROWNUM < 365)
SELECT
DAYS.DDD
FROM
DAYS;
then full outer join between thoes table :
select DUE_DATE , CD_OF_VOLUME , DDD
from (
select
(case when trunc(so.revised_due_date) <= trunc(sysdate)
then trunc(sysdate) else trunc(so.revised_due_date) end) due_date,
(case
when (case when sp.pr_typ in ('VV','VD') then 'DVD' when sp.pr_typ in ('RD','CD')
then 'CD' end) = 'CD'
and (case when so.tec_criteria in ('PI','MC')
then 'XX' else so.tec_criteria end) = 'OF'
then sum(so.revised_qty_due)
end) CD_OF_VOLUME
from shop_order so
left join scm_prodtyp sp
on so.prodtyp = sp.prodtyp
where so.order_type = 'MD'
and so.plant = 'W'
and so.status_code between '4' and '8'
and trunc(so.revised_due_date) <= trunc(sysdate)+30
group by trunc(so.revised_due_date), so.tec_criteria, sp.pr_typ
order by trunc(so.revised_due_date)
) full outer join NEW_TABLE new on ( new .DDD = DUE_DATE )
where new .DDD between /* */ AND /* */ /* pick your own limit) */
you can get the gaps by using connect by and a left join:
assuming your schema is:
create table tbl(DUE_DATE date, CD_OF_VOLUME float);
insert into tbl values(to_date('14/04/2015','DD/MM/YYYY'),35267.00);
insert into tbl values(to_date('15/04/2015','DD/MM/YYYY'),71744.00);
insert into tbl values(to_date('16/04/2015','DD/MM/YYYY'),20268.00);
insert into tbl values(to_date('17/04/2015','DD/MM/YYYY'),35156.00);
insert into tbl values(to_date('18/04/2015','DD/MM/YYYY'),74395.00);
insert into tbl values(to_date('19/04/2015','DD/MM/YYYY'),3636.00);
insert into tbl values(to_date('21/04/2015','DD/MM/YYYY'),5522.00);
insert into tbl values(to_date('22/04/2015','DD/MM/YYYY'),15502.00);
insert into tbl values(to_date('04/05/2015','DD/MM/YYYY'),10082.00);
you can say:
with cte as
(
select (select min(DUE_DATE)-1 from tbl)+ level as dt
from dual
connect by level <= (select max(DUE_DATE)-min(DUE_DATE) from tbl)
)
select to_char(c.dt,'DD/MM/YYYY') gap,null volume
from cte c
left join tbl t on c.dt=t.DUE_DATE
where t.DUE_DATE is null
order by c.dt
Result:
GAP VOLUME
20/04/2015 (null)
23/04/2015 (null)
24/04/2015 (null)
25/04/2015 (null)
26/04/2015 (null)
27/04/2015 (null)
28/04/2015 (null)
29/04/2015 (null)
30/04/2015 (null)
01/05/2015 (null)
02/05/2015 (null)
03/05/2015 (null)
Notice: you can implement this in your original query, one simplest way is to wrap your query and use it as a subquery instead of tbl in above code snippet.
I have only just started looking into SQL.
I have a SQL Server 2008 r2 database that will return two fields DocDate & InvValue. I need to sum the InvValues as MTD & YTD as of Today's Date So it looks like
**Period** /////// **Total value**
MTD ////////////111111.11
YTD /////////////999999.99
I have done a fair amount of Googling and can do one or the other with SUM & DATEPART, but I am stuck with trying to do both.
Can someone give me some pseudo-code that would help me google a little further.
Thank you #Gordon Linoff, That helped a lot and I learned something, That I will find useful in the future.
My code now looks like:
SELECT
SUM(CASE WHEN YEAR(T1.[DocDate]) = YEAR(GETDATE()) THEN T0.[TotalSumSy] END) AS YTD,
SUM(CASE WHEN YEAR(T1.[DocDate]) = YEAR(GETDATE()) AND MONTH(T1.[DocDate]) = MONTH(GETDATE()) THEN T0.[TotalSumSy] END) AS MTD
FROM [dbo].[INV1] T0 INNER JOIN [dbo].[OINV] T1 ON T1.[DocEntry] = T0.[DocEntry]
However I now get
YTD.........MTD
99999.99....111111.11
And I need
YTD........99999.99
MTD........11111.11
Any further assistance would be appreciated.
You can do this with conditional aggregation:
select sum(case when year(docdate) = year(getdate()) then InvValue end) as YTD,
sum(case when year(docdate) = year(getdate()) and month(docdate) = month(getdaate())
then InvValue
end) as MTD
from table t;
This assumes you have no future dates in the table. If you do, add in docdate < getdate() to both clauses.
EDIT:
If you need this in two rows, you can simply do this:
select (case when n.n = 1 then 'YTD' else 'MTD' end) as which,
(case when n.n = 1 then YTD else MTD end) as value
from (select sum(case when year(docdate) = year(getdate()) then InvValue end) as YTD,
sum(case when year(docdate) = year(getdate()) and month(docdate) = month(getdaate())
then InvValue
end) as MTD
from table t
) cross join
(select 1 as n union all select 2) n;
SELECT
Period = 'MTD',
Total_value = SUM(T0.TotalSumSy)
FROM dbo.INV1 T0
INNER JOIN dbo.OINV T1
ON T1.DocEntry = T0.DocEntry
WHERE
T1.DocDate >= DATEADD(month,DATEDIFF(month,'20010101',GETDATE()),'20010101')
AND
T1.DocDate < DATEADD(month,1+DATEDIFF(month,'20010101',GETDATE()),'20010101')
UNION ALL
SELECT
'YTD',
SUM(T0.TotalSumSy)
FROM dbo.INV1 T0
INNER JOIN dbo.OINV T1
ON T1.DocEntry = T0.DocEntry
WHERE
T1.DocDate >= DATEADD(year,DATEDIFF(year,'20010101',GETDATE()),'20010101')
AND
T1.DocDate < DATEADD(year,1+DATEDIFF(year,'20010101',GETDATE()),'20010101') ;
The (complicated) conditions at the WHERE clauses are used instead of the YEAR(column) = YEAR(GETDATE() and the other you had previously, so indexes can be used. WHen you apply a function to a column, you make indexes unsuable (with some minor exceptions for some functions and some verios of SQL-Server.) So, the best thing is to try to convert the conditions to this type:
column <operator> AnyComplexFunction()