SQL: Trying to understand IF/ELSE - sql

SELECT CASE r.SourceId
WHEN '1' THEN 'ITUNES'
WHEN '2' THEN 'SFR'
WHEN '3' THEN 'ORANGE'
ELSE 'Others'
END as source
, CAST(SUM (r.SalesVolume) AS DECIMAL(14, 4) ) AS Volume
, CAST(SUM (r.SalesVolume * r.CustomerPrice) AS DECIMAL(14, 4) ) AS Value
from Rawdata r
INNER JOIN Product p
ON p.ProductId = r.ProductId
INNER JOIN Calendar c
ON r.DayId = c.DayId
WHERE c.WeekId BETWEEN (20145227) AND (20155230)
AND p.ContentFlavor IN ('SD', 'HD')
AND p.VODEST IN ('VOD','EST')
AND p.Distributor IN ('M6SND')
GROUP BY CASE r.SourceId
WHEN '1' THEN 'ITUNES'
WHEN '2' THEN 'SFR'
WHEN '3' THEN 'ORANGE'
ELSE 'Others'
END
The result of the above query is:
source Volume Value
ITUNES 48316.0000 506067.2600
This result is perfectly OK since my source table RawData doesnt contain any values for SourceId 2 or 3.
But what I basically want is the result to look like is:
source Volume Value
ITUNES 48316.0000 506067.2600
SFR 0 0
ORANGE 0 0
Others 0 0
If there is no value corresponding to any column parameter then I need it to be 0
I assume this could be done using IF/ELSE but not sure how?

with the help of a CTE this is a way to do it. (replace the first query with something more dynamic if you want)
with myChoices (choices)
as (
select
choices
from (
values
('ITUNES'),
('SFR'),
('ORANGE'),
('Others')
) [ ] (choices)
),
myQuery ([source],[Volume],[Value])
as (
SELECT CASE r.SourceId
WHEN '1' THEN 'ITUNES'
WHEN '2' THEN 'SFR'
WHEN '3' THEN 'ORANGE'
ELSE 'Others'
END as source
, CAST(SUM (r.SalesVolume) AS DECIMAL(14, 4) ) AS Volume
, CAST(SUM (r.SalesVolume * r.CustomerPrice) AS DECIMAL(14, 4) ) AS Value
from Rawdata r
INNER JOIN Product p
ON p.ProductId = r.ProductId
INNER JOIN Calendar c
ON r.DayId = c.DayId
WHERE c.WeekId BETWEEN (20145227) AND (20155230)
AND p.ContentFlavor IN ('SD', 'HD')
AND p.VODEST IN ('VOD','EST')
AND p.Distributor IN ('M6SND')
GROUP BY CASE r.SourceId
WHEN '1' THEN 'ITUNES'
WHEN '2' THEN 'SFR'
WHEN '3' THEN 'ORANGE'
ELSE 'Others'
END
)
select
c.choices,
ISNULL(q.Volume,0)Volume,
ISNULL(q.Value,0)Value
from myChoices c
left join myQuery q on
c.choices = q.[source]

Create an inline view called "Product_Inline_View", which is like
(select 1 as SourceId, 'ITUNES' as source_name
union all
select 2 as SourceId, 'SFR' as source_name
union all
select 3 as SourceId, 'ORANGE' as source_name
)
Right Join the Product_Inline_view with the Query you have, but without the CASE.
And then do the group by.

Related

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

How do I Separate One Column values into Multiple rows based on condition in SQL

How to Seperate column values into multiple rows based on Condition. Resultant table will show in Grid view. I tried with Count(*) in multiple select statements but not what i expected. Thanks in advance
Table: RegistrationReport
Date Type
-----------------------------------------
02/05/2015 A
04/05/2015 B
04/05/2015 C
05/05/2015 A
I need output like this:
Date Type 1 Type 2 Type 3
--------------------------------------------------
02/05/2015 A - -
04/05/2015 - B -
04/05/2015 - - C
05/05/2015 A - -
--------------------------------------------------
Total: 2 1 1
Try below mentioned simple query to get Total as well.
;with CTE as(
SELECT Date
,case when Type = 'A' then 'A' else '-' end as 'Type_1'
, case when Type = 'B' then 'B' else '-' end as 'Type_2'
, case when Type = 'C' then 'C' else '-' end as 'Type_3'
FROM RegistrationReport
)
select cast(Date as varchar(20))Date
,Type_1
,Type_2
,Type_3
from CTE
UNION ALL
SELECT 'Total:'
,CAST(SUM(case when Type_1= 'A' then 1 else 0 end)as varchar(10))
,CAST(SUM(case when Type_2= 'B' then 1 else 0 end)as varchar(10))
,CAST(SUM(case when Type_3= 'C' then 1 else 0 end) as varchar(10))
FROM CTE
You will get required output!
Assuming you know the number of columns, and it is relatively few, I think the easiest solution is to just self join:
Select distinct Cast(coalesce(a.date, b.date, c.date) as varchar) as Date
, isnull(a.Type, '--') as Type1
, isnull(b.Type, '--') as Type2
, isnull(c.Type, '--') as Type3
from Table a
full outer join Table b
on a.date = b.date
full outer join Table c
on isnull(a.date, b.date) = c.date
where isnull(a.type, 'A') = 'A'
and isnull(b.type, 'B') = 'B'
and isnull(c.type, 'C') = 'C'
union all
select 'Total'
, count(distinct case when type = 'A' then Date end)
, count(distinct case when type = 'B' then Date end)
, count(distinct case when type = 'C' then Date end)
from Table

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.

Can I add multiple summary rows in TSQL?

Goal: I am trying to add a summary row per school and I have this set up in Fiddle.
Needs:
List item I would like the enrollment totals for each year for Smith Elementary, Jones Elementary, etc.
List item I would also like to have summary rows for all levels (ES,MS,HS).
List item I would also like to have a grand total row for all levels.
Fiddle:
Is this possible in TSQL? I'm running SQL Server 2008 R2.
SELECT
schoolid,
sitename,
level,
area,
grade,
20122013ActualEnrollment ,
20132014ActualEnrollment,
20152016ActualEnrollment
FROM supportContacts
ORDER BY
CASE
WHEN Level= 'ES' THEN '1'
WHEN Level= 'MS' THEN '2'
WHEN Level= 'HS' THEN '3'
ELSE '4'
END
, SiteName
, CASE
WHEN Grade = 'K' THEN '1'
WHEN Grade = '1' THEN '2'
WHEN Grade = '2' THEN '3'
WHEN Grade = '3' THEN '4'
WHEN Grade = '4' THEN '5'
WHEN Grade = '5' THEN '6'
ELSE '7'
END
Output:
Thank you for your help!
Please realize this is pretty much a shot in the dark. I don't have the structure or sample data when writing this.
SELECT
SY12.[SchoolID]
, MAX(SY12.[SiteName])
, MAX(SY12.[Level])
, MAX(S.[AreaName])
, SY12.[Grade]
, SY12.[TotalEnrollment] AS '2012-2013 Actual Enrollment'
, SY13.[TotalEnrollment] AS '2013-2014 Actual Enrollment'
, P.[2016] AS '2015-2016 Projected Enrollment'
FROM
[2012Cycle1_Data] AS SY12
LEFT OUTER JOIN
[2013Cycle1_Data] AS SY13 ON SY12.SchoolID = SY13.[SchoolID]
AND SY12.Grade = SY13.Grade
LEFT OUTER JOIN
[Projections] AS P ON SY12.SchoolID = P.[SchoolID]
AND SY12.Grade = P.Grade
LEFT OUTER JOIN
v_Sites AS S ON SY12.SchoolID = S.SchoolID
GROUP BY SY12.[SchoolID], SY12.[Grade]
ORDER BY
CASE
WHEN SY12.Level= 'ES' THEN '1'
WHEN SY12.Level= 'MS' THEN '2'
WHEN SY12.Level= 'HS' THEN '3'
ELSE '4'
END
, SiteName
, CASE
WHEN SY12.Grade = 'K' THEN '1'
WHEN SY12.Grade = '1' THEN '2'
WHEN SY12.Grade = '2' THEN '3'
WHEN SY12.Grade = '3' THEN '4'
WHEN SY12.Grade = '4' THEN '5'
WHEN SY12.Grade = '5' THEN '6'
ELSE '7'
END
Looks like I have typos in the query. :(
SELECT
schoolid,
MAX(sitename),
MAX(level),
MAX(area),
grade,
SUM(20122013ActualEnrollment),
SUM(20132014ActualEnrollment),
SUM(20152016ActualEnrollment)
FROM supportContacts
GROUP BY schoolid, grade WITH ROLLUP
That worked in your fiddle. I know it is not ordered by but maybe you can run with it?

SQL Select Sum Query

I need to complete the following but need to SUM on EntryValue where EntryType= 'C' - EntryType = 'D' but not sure where to include it.
SELECT SUM(EntryValue) AS EntryValue, Left(Warehouse,1) AS Branch
FROM GenJournalDetail INNER JOIN InvJournalCtl ON (GenJournalDetail.GlPeriod = InvJournalCtl.LedgPeriod) AND (GenJournalDetail.GlYear = InvJournalCtl.YearPostLedg) AND (GenJournalDetail.Journal = InvJournalCtl.GlJournal)
WHERE GenJournalDetail.GlCode = 'Z5207550' AND GlYear = '2011' AND GlPeriod = '10'
GROUP BY Left(InvJournalCtl.Warehouse,1)
Select it then group it;
SELECT SUM(EntryValue) AS EntryValueTotal, EntryType, Left(Warehouse,1) AS Branch
...
GROUP BY Left(InvJournalCtl.Warehouse,1), EntryType
Edit I didn't realise you wanted to deduct;
SELECT
SUM(CASE EntryType
WHEN 'C' THEN EntryValue
ELSE 0 END
)
-
SUM(CASE EntryType
WHEN 'D' THEN EntryValue
ELSE 0 END
) AS EntryValueTotal,
Left(Warehouse,1) AS Branch
FROM GenJournalDetail
INNER JOIN InvJournalCtl ON (GenJournalDetail.GlPeriod = InvJournalCtl.LedgPeriod) AND (GenJournalDetail.GlYear = InvJournalCtl.YearPostLedg) AND (GenJournalDetail.Journal = InvJournalCtl.GlJournal)
WHERE
GenJournalDetail.GlCode = 'Z5207550' AND GlYear = '2011' AND GlPeriod = '10'
GROUP BY Left(InvJournalCtl.Warehouse,1)
If you need to sum the column add it in the select and group:
SELECT SUM(EntryValue) AS TotEntry, ...
...
GROUP BY ...
you don't need to group an aggregated column; you need to filter query too?