Missing row when data value is 0 in teradata - sql

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

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>)

TOP 1 or LIMIT 1 not working in a Correlated Subquery - SPS 11

I am trying to work on a query to fill the Price for a product where it is zero with non-zero value fro previous record. I tried to write a simple correlated subquery but its not working.
var_1 = select * from "XYZ"."PRD_TEST" where price <> 0 order by period desc;
var_out = select a.product,a.period, ( select price from :var_1 b where a.product = b.product and a.period > b.period and b.period <> 0 limit 1 ) as price from "XYZ"."PRD_TEST" a;
PRODUCT PERIOD PRICE
A 1 100
A 2 0 - to be filled with 100
A 3 0 - to be filled with 100
A 4 5
A 5 0 - to be filled with 5
I tried to replace the sub-query with scalar function but it does not take table as a Parameter.
I tried to achieve the output using Left outer join and Row_number but it's too expensive and runs for a long time.
I am looking for a best option to fetch only 1 record in the subquery just like TOP 1.
you could use a scalar subquery like this ( select ... limit 1 is not considered scalar in HANA, unfortunately):
Do begin
var_1 = select * from (( Select 'A' product, '1' period, '100' price from sys.dummy )
Union ( Select 'A' product, '2' period, '0' price from sys.dummy )
Union ( Select 'A' product, '3' period, '0' price from sys.dummy )
Union ( Select 'A' product, '4' period, '5' price from sys.dummy )
Union ( Select 'A' product, '5' period, '0' price from sys.dummy )) order by period desc;
var_out = ( select a.product,
a.period,
( select max(price)
from :var_1 b
where a.product = b.product
and a.period > b.period
and b.period <> 0
and b.period = ( select max(period) from :var_1 c
where a.product = c.product
AND a.period > c.period
and c.period <> 0
and c.price <> 0
)) as price
from :var_1 a where price = '0' )
union (select product, period, price from :var_1 where price <> '0' );
select * from :var_out order by product, period;
end
Tested on sps12
Added after comment.
Why don't you just try? It is very simple.
Because I was curious I tried it on my HCP trial instance, takes about 1 sec for on milion rows. I included a ifnull to avoid rows with null-values where there is no price in earlier periods.
Here is the coding:
drop table var_1;
create column table var_1 as
(
select
cast ( 'Prod' || prd.GENERATED_PERIOD_START as nvarchar(20) ) product,
cast ( per.GENERATED_PERIOD_START as decimal(2)) period,
cast ( case when rand() < '0.5' then rand() * '100' else '0' end
as decimal(5,2)) as price -- ~50% of price is 0
from series_generate_integer(1,0,1000000/13) as prd, --~1Mio records
series_generate_integer(1,0,13) as per --12 periods + period 0
);
merge delta of var_1;
select * from var_1
order by product, period
limit 100;
do begin sequential execution -- don't let parallel execution influence the runtime-measurement
declare start_timestamp timestamp;
start_timestamp = current_timestamp;
var_out = ( select a.product,
a.period,
ifnull ((select max(price)
from var_1 b
where a.product = b.product
and a.period > b.period
and b.period <> 0
and b.period = ( select max(period) from var_1 c
where a.product = c.product
AND a.period > c.period
and c.period <> 0
and c.price <> 0
)),'0.0') as price
from var_1 a where price = '0' )
union (select product, period, price from var_1 where price <> '0' );
select nano100_between(:start_timestamp, (select current_timestamp from dummy) )/10000 as runtime_millisec from dummy;
select * from :var_out
order by product, period
limit 100;
end

sql join and group by generated date range

I have Table1 and I need a query to populate Table2:
Problem here is with Date column. I want to know the process of location/partner combination per day. Main issue here is that I can't pick DateCreated and make it as default date since it doesn't necessarily cover whole date range, like in this example where it doesn't have 2015-01-07 and 2015-01-09. Same case with other dates.
So, my idea is to first select dates from some table which contains needed date range and then perform calculation for each day/location/partner combination from cte but in that case I can't figure out how to make a join for LocationId and PartnerId.
Columns:
Date - CreatedItems - number of created items where Table1.DateCreated = Table2.Date
DeliveredItems - number of delivered items where Table1.DateDateOut = Table2.Date
CycleTime - number of days delivered item was in the location (DateOut - DateIn + 1)
I started with something like this but it's very like that I completely missed the point with it:
with d as
(
select date from DimDate
where date between DATEADD(DAY, -365, getdate()) and getdate()
),
cr as -- created items
(
select
DateCreated,
LocationId,
PartnerId,
CreatedItems = count(*)
from Table1
where DateCreated is not null
group by DateCreated,
LocationId,
PartnerId
),
del as -- delivered items
(
select
DateOut,
LocationId,
ParnerId,
DeliveredItems = count(*),
CycleTime = DATEDIFF(Day, DateOut, DateIn)
from Table1
where DateOut is not null
and Datein is not null
group by DateOut,
LocationId,
PartnerId
)
select
d.Date
from d
LEFT OUTER JOIN cr on cr.DateCreated = d.Date -- MISSING JOIN PER LocationId and PartnerId
LEFT OUTER JOIN del on del.DateCompleted = d.Date -- MISSING JOIN PER LocationId and PartnerId
with range(days) as (
select 0 union all select 1 union all select 2 union all
select 3 union all select 4 union all select 5 union all
select 6 /* extend as necessary */
)
select dateadd(day, r.days, t.DateCreated) as "Date", locationId, PartnerId,
sum(
case
when dateadd(day, r.days, t.DateCreated) = t.DateCreated
then 1 else 0
end) as CreatedItems,
sum(
case
when dateadd(day, r.days, t.DateCreated) = t.Dateout
then 1 else 0
end) as DeliveredItems,
sum(
case
when dateadd(day, r.days, t.DateCreated) = t.Dateout
then datediff(days, t.DateIn, t.DateOut) + 1 else 0
end) as CycleTime
from
<yourtable> as t
inner join range as r
on r.days between 0 and datediff(day, t.DateCreated, t.DateOut)
group by dateadd(day, r.days, t.DateCreated), LocationId, PartnerId;
If you only want the end dates (rather than all the dates in between) this is probably a better approach:
with range(dt) as (
select distinct DateCreated from T union
select distinct DateOut from T
)
select r.dt as "Date", locationId, PartnerId,
sum(
case
when r.dt = t.DateCreated
then 1 else 0
end) as CreatedItems,
sum(
case
when r.dt = t.Dateout
then 1 else 0
end) as DeliveredItems,
sum(
case
when r.dt = t.Dateout
then datediff(days, t.DateIn, t.DateOut) + 1 else 0
end) as CycleTime
from
<yourtable> as t
inner join range as r
on r.dt in (t.DateCreated, t.DateOut)
group by r.dt, LocationId, PartnerId;
If to specify WHERE clause? Something Like that:
WHERE cr.LocationId = del.LocationId AND
cr.PartnerId = del.PartnerId

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.

Oracle SQL compare dates to list

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;