Oracle SQL compare dates to list - sql

I have a list of model, serial, dates (the dates do not exist in the DB). I need to pull model, serial, and most recent call dates prior to the dates given to me. I did find a "solution" that worked using CASE but was wondering if there is a simpler way. When having a list of a 100+ items the OR CASE statements are a bit grueling to work with.
SELECT M.MODEL_NBR, DEV.SERIAL, i.c_date
FROM device dev,
model m,
A_DEVICE ad,
INTR i
WHERE (DEV.SERIAL, i.intr_id) IN
(select serial, max(t.intr_id) over (partition by t.part_id)
from intr t, device d
where t.part_id = AD.PART_ID
and d.device_id = ad.device_id
and d.model_type = 'XX'
and d.serial in ('1234', '5678')
and (1 = (case
when d.serial = '1234'
and t.c_date < to_date('10/10/2014', 'mm/dd/yyyy')
then 1
end)
or 1 = (case
when d.serial = '5678'
and t.c_date < to_date('11/11/2014', 'mm/dd/yyyy')
then 1
end)))
AND M.MODEL_ID = DEV.MODEL_ID
and M.MODEL_NBR = '1111'
AND DEV.DEVICE_ID = AD.DEVICE_ID
AND AD.PART_ID = I.PART_ID
order by DEV.SERIAL;

A CASE in the WHERE clause is almost always better represented as AND/OR expressions. In this case, instead of:
and d.serial in ('1234', '5678')
and (1 = (case
when d.serial = '1234'
and t.c_date < to_date('10/10/2014', 'mm/dd/yyyy')
then 1
end)
or 1 = (case
when d.serial = '5678'
and t.c_date < to_date('11/11/2014', 'mm/dd/yyyy')
then 1
end)))
You'd be better off using:
and (( d.serial = '1234'
and t.c_date < to_date('10/10/2014', 'mm/dd/yyyy'))
or (d.serial = '5678'
and t.c_date < to_date('11/11/2014', 'mm/dd/yyyy'))
Alternately, you could use a CTE to make a pseudo-table using your values:
WITH serial_values as (
select '1234' as serial, to_date('10/10/2014', 'mm/dd/yyyy') as c_date from dual
union all
select '5678' as serial, to_date('11/11/2014', 'mm/dd/yyyy') as c_date from dual)
SELECT M.MODEL_NBR, DEV.SERIAL, i.c_date
FROM device dev,
model m,
A_DEVICE ad,
INTR i
WHERE (DEV.SERIAL, i.intr_id) IN
(select serial, max(t.intr_id) over (partition by t.part_id)
from intr t
cross join device d
join serial_values sv
on d.serial = sv.serial
and t.c_date < sv.c_date
where t.part_id = AD.PART_ID
and d.device_id = ad.device_id
and d.model_type = 'XX')
AND M.MODEL_ID = DEV.MODEL_ID
and M.MODEL_NBR = '1111'
AND DEV.DEVICE_ID = AD.DEVICE_ID
AND AD.PART_ID = I.PART_ID
order by DEV.SERIAL;

Related

How can I get different data with case?

In view If lokasyonno= 27 , I want the first block to run, if not the second block. How can I do it?
I tried to do it with the switch case structure, but I couldn't get a result because there is no common column.
FIRST
SELECT round(UH.NETTUTAR)
FROM TBL_IRSALIYE IR
INNER JOIN TBL_URUNHAREKETLERI UH ON UH.IRSALIYEID = IR.IRSALIYEID
INNER JOIN TBL_URUNLER U ON UH.URUNID = U.URUNID
WHERE IR.LOKASYONNO = 27
AND IR.TUR = 7
AND IR.IPTAL = 0
AND IR.ONAY = 1
AND IR.BILLED = 1
AND IR.INTERNET = 60
AND ROWNUM <= 10
AND IR.TARIH > TO_DATE ('2020-09-27 ', 'yyyy-mm-dd ')
AND IR.TARIH <= TO_DATE ('2020-10-05', 'yyyy-mm-dd ')
second
NVL (
(SELECT ROUND (
SUM (
CASE DOCUMENT_TYPE
WHEN 2
THEN
(CASE TRANSACTION_TYPE
WHEN 0
THEN
0
- ( LINE_TOTAL_VALUE
- LINE_TOTL_DISCOUNT)
ELSE
LINE_TOTAL_VALUE
- LINE_TOTL_DISCOUNT
END)
ELSE
(CASE TRANSACTION_TYPE
WHEN 1
THEN
0
- ( LINE_TOTAL_VALUE
- LINE_TOTL_DISCOUNT)
ELSE
( LINE_TOTAL_VALUE
- LINE_TOTL_DISCOUNT)
END)
END),
10)
FROM TBL_TRANSACTION_LINES
WHERE (TRANSACTION_TYPE NOT IN (10, 30))
AND STORE_NO = LOK.LOKASYONNO
AND (URUNID = TBL_URUNLER.URUNID)
AND TRANSACTION_DATE >
TO_DATE ('2020-09-27 0:0:0',
'yyyy-mm-dd HH24:MI:SS')
AND TRANSACTION_DATE <=
TO_DATE ('2020-10-04 0:0:0',
'yyyy-mm-dd HH24:MI:SS')),
0)
AS HAFTALIKKASASATISTUTARI
Use a union of the two queries but add a not exists() on the first query as an additional condition of the second query:
<first query>
union all
<second query>
where not exists (<first query>)

Missing row when data value is 0 in teradata

I am trying to do a group-by in a query but every time the data has a 0, the group by does not show the entire row. How do I fix this?
This is the regular query
select
COUNT(DISTINCT sr.sr_number) AS NEW_CASES
FROM table sr
where
sr.business_unit in('BU1')
and OPENED_DATE < trunc(current_Date)
and OPENED_DATE > trunc(current_date -2)
and sr.status = 'Open'
The output is
NEW_CASES
0
But when I do a group by. The entire row is gone.
select
COUNT(DISTINCT sr.sr_number) AS NEW_CASES,
sr.business_unit
FROM table sr
where
sr.business_unit in('BU1','BU2','BU3' )
and OPENED_DATE < trunc(current_Date)
and OPENED_DATE > trunc(current_date -2)
and sr.status = 'Open'
group by sr.business_unit
The group by output is
New_CASES BUSINESS_UNIT
200 BU2
300 BU3
Desired output:
New_CASES BUSINESS_UNIT
0 BU1
200 BU2
300 BU3
One option is to start from a fixed list of values, and then bring the table with a left join, like so:
select b.business_unit, count(distinct t.sr_number) as new_cases
from (
select 'bu1' business_unit from dual
union all select 'bu2' from dual
union all select 'bu3' from dual
) b
left join mytable t
on t.business_unit = b.business_unit
and t.opened_date > trunc(current_date -2)
and t.opened_date < trunc(current_date)
and t.status = 'open'
group by b.business_unit
In Teradata, the syntax is somehow cumbersome:
select b.business_unit, count(distinct t.sr_number) as new_cases
from (
select * from (select 'bu1' as business_unit) x
union all select * from (select 'bu2' as business_unit ) x
union all select * from (select 'bu3' as business_unit ) x
) b
left join mytable t
on t.business_unit = b.business_unit
and t.opened_date > trunc(current_date -2)
and t.opened_date < trunc(current_date)
and t.status = 'open'
group by b.business_unit
Use a left join. In Oracle, this would look like
select b.business_unit, COUNT(DISTINCT sr.sr_number) AS NEW_CASES
from (select 'BU1' as business_unit from dual union all
select 'BU2' as business_unit from dual union all
select 'BU3' as business_unit from dual
) b left join
sr
on sr.business_unit = b.business_unit and
sr.OPENED_DATE < trunc(current_Date) and
sr.OPENED_DATE > trunc(current_date -2) and
sr.status = 'Open'
group by b.business_unit
EDIT:
Teradata doesn't have a really convenient way to create a derived table of constant values, but you can do it:
select b.business_unit, COUNT(DISTINCT sr.sr_number) AS NEW_CASES
from (select 'BU1' as business_unit from (select 1 as dummy) t union all
select 'BU2' as business_unit from (select 1 as dummy) t union all
select 'BU3' as business_unit from (select 1 as dummy) t
) b left join
sr
on sr.business_unit = b.business_unit and
sr.OPENED_DATE < trunc(current_Date) and
sr.OPENED_DATE > trunc(current_date -2) and
sr.status = 'Open'
group by b.business_unit

ORACLE SQL: Fill in missing dates

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.

SQL: Filtering a query with multiple conditions

As I'm sure you'll be able to tell from this question, I am very new and unfamiliar with SQL. After quite some time (and some help from this wonderful website) I was able to create a query that lists almost exactly what I want:
Select p1.user.Office,
p1.user.Loc_No,
p1.user.Name,
p1.user.Code,
p1.user.Default_Freq,
(Select distinct MAX(p2.pay.Paycheck_PayDate)
from p2.pay
where p1.user.Client_TAS_CL_UNIQUE = p2.pay.CL_UniqueID) as Last_Paycheck
from
PR.client
where
p1.user.Client_End_Date is null
and p1.user.Client_Region = 'Z'
and p1.user.Client_Office <> 'ZF'
and substring(p1.user. Code,2,1) <> '0'
Now I just need to filter this slightly more using the following logic:
If Default_Freq = 'W' then only output clients with a Last_Paycheck 7 or more days past the current date
If Default_Freq = 'B' then only output clients with a Last_Paycheck 14 or more days past the current date
Etc., Etc.
I know this is possible, but I have no clue how the syntax should start. I believe I would need to use a Case statement inside the Where clause? Any help is greatly appreciated as always!
SELECT
X.p1.user.Office,
X.p1.user.Loc_No,
X.p1.user.Name,
X.p1.user.Code,
X.Default_Freq,
X.Last_Paycheck
FROM
(Select
p1.user.Office,
p1.user.Loc_No,
p1.user.Name,
p1.user.Code,
p1.user.Default_Freq AS Default_Freq,
(Select distinct MAX(p2.pay.Paycheck_PayDate)
from p2.pay
where p1.user.Client_TAS_CL_UNIQUE = p2.pay.CL_UniqueID) as Last_Paycheck
from
PR.client
where
p1.user.Client_End_Date is null
and p1.user.Client_Region = 'Z'
and p1.user.Client_Office <> 'ZF'
and substring(p1.user. Code,2,1) <> '0') X
WHERE (
(X.Default_Freq = 'W' AND (DATEDIFF ( d , X.Last_Paycheck , #currentDate) >= 7))
OR (X.Default_Freq = 'B' AND (DATEDIFF ( d , X.Last_Paycheck , #currentDate) >= 14))
)
Use your current query as a derived table or a CTE (CTEs exists for SQL Server 2005+). And then you can do:
;WITH CTE AS
(
--- Your current query here
)
SELECT *
FROM CTE
WHERE (Default_Freq = 'W' AND DATEDIFF(DAY,Last_Paycheck,GETDATE()) >= 7)
OR (Default_Freq = 'B' AND DATEDIFF(DAY,Last_Paycheck,GETDATE()) >= 14)
OR () -- keep going
Make a case statement, and put that in your where clause.
I.e. (off the top of my head):
CASE
WHEN (Default_Freq = 'W' AND DATEDIFF(DAY,Last_Paycheck,GETDATE()) >= 7) THEN 1
WHEN (Default_Freq = 'B' AND DATEDIFF(DAY,Last_Paycheck,GETDATE()) >= 14) THEN 1
ELSE 0 END = 1
try this with inner query:
select * from (
Select
p1.user.Office,
p1.user.Loc_No,
p1.user.Name,
p1.user.Code,
p1.user.Default_Freq,
(Select distinct MAX(p2.pay.Paycheck_PayDate)
from p2.pay
where p1.user.Client_TAS_CL_UNIQUE = p2.pay.CL_UniqueID) as Last_Paycheck
from PR.client
where p1.user.Client_End_Date is null
and p1.user.Client_Region = 'Z'
and p1.user.Client_Office <> 'ZF'
and substring(p1.user. Code,2,1) <> '0') t
where (
(Default_Freq = 'W' AND (DATEDIFF ( d , Last_Paycheck , #currentDate) <= 7))
OR (Default_Freq = 'B' AND (DATEDIFF ( d , Last_Paycheck , #currentDate) <= 14))
)

counting events over flexible ranges

I am trying to count events (which are rows in the event_table) in the year before and the year after a particular target date for each person. For example, say I have a person 100 and target date is 10/01/2012. I would like to count events in 9/30/2011-9/30/2012 and in 10/02/2012-9/30/2013.
My query looks like:
select *
from (
select id, target_date
from subsample_table
) as i
left join (
select id, event_date, count(*) as N
, case when event_date between target_date-365 and target_date-1 then 0
when event_date between target_date+1 and target_date+365 then 1
else 2 end as after
from event_table
group by id, target_date, period
) as h
on i.id = h.id
and i.target_date = h.event_date
The output should look something like:
id target_date after N
100 10/01/2012 0 1000
100 10/01/2012 1 0
It's possible that some people do not have any events in the before or after periods (or both), and it would be nice to have zeros in that case. I don't care about the events outside the 730 days.
Any suggestions would be greatly appreciated.
I think the following may approach what you are trying to accomplish.
select id
, target_date
, event_date
, count(*) as N
, SUM(case when event_date between target_date-365 and target_date-1
then 1
else 0
end) AS Prior_
, SUM(case when event_date between target_date+1 and target_date+365
then 1
else 0
end) as After_
from subsample_table i
left join
event_table h
on i.id = h.id
and i.target_date = h.event_date
group by id, target_date, period
This is a generic answer. I don't know what date functions teradata has, so I will use sql server syntax.
select id, target_date, sum(before) before, sum(after) after, sum(righton) righton
from yourtable t
join (
select id, target_date td
, case when yourdate >= dateadd(year, -1, target_date)
and yourdate < target_date then 1 else 0 end before
, case when yourdate <= dateadd(year, 1, target_date)
and yourdate > target_date then 1 else 0 end after
, case when yourdate = target_date then 1 else 0 end righton
from yourtable
where whatever
group by id, target_date) sq on t.id = sq.id and target_date = dt
where whatever
group by id, target_date
This answer assumes that an id can have more than one target date.