convert rows into column in db2 - db2

I need the below select results to be converted into single row ,
Actual output:
ORDER POSTCODE Quantity Value
123456 AAAAA 22.78 5
123456 AAAAA 2.93 7
Expected Output:
ORDER POSTCODE AmbientQuantity Ambientvalue FVQuantity FvVAlue
123456 AAAAA 22.78 5 2.93 7
How to achieve the expected output in db2?

Following SQL will do the job:
with temp as (
select ORDER, POSTCODE, QUANTITY, VALUE,
rownumber() over (partition by ORDER, POSTCODE) as rownum
FROM mytable2
)
select ORDER, POSTCODE,
max(case when rownum = 1 Then QUANTITY end) as AMBIENTQUANTITY,
max(case when rownum = 1 Then VALUE end) as AMBIENTVALUE,
max(case when rownum = 2 Then QUANTITY end) as FVQuantity,
max(case when rownum = 2 Then VALUE end) as FVVALUE
from temp
group by ORDER, POSTCODE

Try something like this:
with tmp as (
select f0.*, rownumber() over(partition by f0.ORDER, f0.POSTCODE) rang
from your table f0
)
select
f1.ORDER, f1.POSTCODE, f1.Quantity AmbientQuantity, f1.Value Ambientvalue,
f2.Quantity FVQuantity2, f2.Value FvVAlue2,
f3.Quantity FVQuantity3, f2.Value FvVAlue3,
f4.Quantity FVQuantity4, f2.Value FvVAlue4
from tmp f1
left outer join tmp f2 on (f1.ORDER, f1.POSTCODE)=(f2.ORDER, f2.POSTCODE) and f2.rang=2
left outer join tmp f3 on (f1.ORDER, f1.POSTCODE)=(f3.ORDER, f3.POSTCODE) and f3.rang=3
left outer join tmp f4 on (f1.ORDER, f1.POSTCODE)=(f4.ORDER, f4.POSTCODE) and f4.rang=4
where f1.rang=1

Related

Postgresql, combine different columns counts into one result?

I have Car table. Car has is_sold and is_shipped. A Car belongs to a dealership, dealership_id (FK).
I want to run a query that tells me the count of sold cars and the count of shipped cars for a given dealership all in one result.
sold_count | shipped_count
10 | 4
The single queries I have look like this:
select count(*) as sold_count
from car
where dealership_id=25 and is_sold=true;
and
select count(*) as shipped_count
from car
where dealership_id=25 and is_shipped=true;
How do I combine the two to get both counts in one result?
This will do:
select dealership_id,
sum(case when is_sold is true then 1 else 0 end),
sum(case when is_shipped is true then 1 else 0 end)
from cars group by dealership_id;
You can use the filter clause of the Aggregate function. (see demo)
select dealership_id
, count(*) filter (where is_sold) cars_sold
, count(*) filter (where is_shipped) cars_shipped
from cars
where dealership_id = 25
group by dealership_id;
You can also using cross join.
select 'hello' as col1, 'world' as col2;
return:
col1 | col2
-------+-------
hello | world
(1 row)
similarly,
with a as
(
select count(*) as a1 from emp where empid> 5),
b as (
select count(*) as a2 from emp where salary > 6000)
select * from a, b;
or you can even apply to different table. like:
with a as
(select count(*) as a1 from emp where empid> 5),
b as
(select count(*) as a2 from ab )
select * from a, b;
with a as
(
select count(*) as sold_count
from car
where dealership_id=25 and is_sold=true
),
b as
(
select count(*) as shipped_count
from car
where dealership_id=25 and is_shipped=true
)
select a,b;
further reading: https://www.postgresql.org/docs/current/queries-table-expressions.html.
https://stackoverflow.com/a/26369295/15603477

Aggregating columns and getting count of values as row

I have a query output as below:
ID ID2 Working Leave Off Day
14595 76885302 10 0 0
178489 78756208 0 0 1
178489 78756208 0 1 0
I want to receive an output like below:
ID ID2 code value
14595 76885302 Working 10
178489 78756208 Off day 1
178489 78756208 Leave 1
My query is like below:
select tei.organisationunitid,pi.trackedentityinstanceid as tei,
count(case when tedv.value = 'Working' then tedv.value end) Working,
count(case when tedv.value = 'Off day' then tedv.value end) Offday,
count(case when tedv.value = 'Leave' then tedv.value end) Leave
from programstageinstance psi
inner join programinstance pi on pi.programinstanceid = psi.programinstanceid
inner join trackedentitydatavalue tedv on tedv.programstageinstanceid = psi.programstageinstanceid
inner join dataelement de on de.dataelementid = tedv.dataelementid
inner join trackedentityinstance tei on tei.trackedentityinstanceid = pi.trackedentityinstanceid
where psi.executiondate between '2017-01-01' and '2019-06-01'
and de.uid in ('x2222EGfY4K')
and psi.programstageid in (select programstageid
from programstage
where uid = 'CLoZpO22228')
and tei.organisationunitid in (select organisationunitid
from organisationunit
where path like '%Spd2222fvPr%')
group by pi.trackedentityinstanceid,de.uid,tei.organisationunitid,tedv.value
How can I achieve this?
I would try the JSON approach. I made a step-by-step fiddle:
demo:db<>fiddle
SELECT
id, id2,
elements ->> 'code' AS code,
SUM((elements ->> 'value')::int) AS value
FROM (
SELECT
id,
id2,
json_build_object('code', 'working', 'value', working) AS working,
json_build_object('code', 'leave', 'value', leave) AS leave,
json_build_object('code', 'off_day', 'value', off_day) AS off_day
FROM
mytable
) s,
unnest(ARRAY[working, leave, off_day]) as elements
GROUP BY 1,2,3
HAVING SUM((elements ->> 'value')::int) > 0

tsql -group by by max datetime in rows

I have a table and it's data looks like this:
id name date
--------- --------- ----------
1 a 2012-08-30 10:36:27.393
1 b 2012-08-30 14:36:27.393
2 c 2012-08-30 13:36:27.393
2 d 2012-08-30 16:36:27.393
I retrieve the max datetime with this query:
SELECT id,Max(date) as mymaxdate
FROM table1
group by id
This query givse me two rows like this:
1 2012-08-30 14:36:27.393
2 2012-08-30 16:36:27.393
It's correct, but how can i change it to retrieve this result?
1 b 2012-08-30 14:36:27.393
2 d 2012-08-30 16:36:27.393
Thanks
For SQL Server 2005+
WITH cteMaxDate AS (
SELECT id, name, date,
ROW_NUMBER() OVER(PARTITION BY id ORDER BY date DESC) AS RowNum
FROM table1
)
SELECT id, name, date
FROM cteMaxDate
WHERE RowNum = 1;
One of the options:
select
t1.id
,t1.name
,t1.date
from
table1 t1
inner join (
SELECT id,Max(date) as mymaxdate
FROM table1
group by id
) mt1
on t1.id = mt1.id
and t1.date = mt1.mymaxdate

Merging tables in t-sql

I have a table holding periods and prices, something like this
itemid periodid periodstart periodend price
1 1 2011/01/01 2011/05/01 50.00
1 2 2011/05/02 2011/08/01 80.00
1 3 2011/08/02 2011/12/31 50.00
Now I have a second table that can hold single dates or periods
itemid periodid periodstart periodend price
1 8 2011/07/01 2011/07/17 70.00
Now, how can I do a query that would return the following result?
itemid periodid periodstart periodend price
1 1 2011/01/01 2011/05/01 50.00
1 2 2011/05/02 2011/06/30 80.00 ****
1 8 2011/07/01 2011/07/17 70.00 ***
1 2 2011/07/18 2011/08/01 80.00 ****
1 3 2011/08/02 2011/12/31 50.00
EDIT -- Highlight the fact that the merge is modifying the dates around it
How about something like
select
t1.itemid,t1.periodid,t1.periodstart, coalesce(dateadd(d,-1,t2.periodstart),t1.periodend) as periodend, t1.price
from t1
left outer join t2 on t1.periodstart < t2.periodstart and t1.periodend>t2.periodstart and t1.itemid=t2.itemid
union
select
t2.itemid,t2.periodid,t2.periodstart, t2.periodend, t2.price
from t1
inner join t2 on t1.periodstart < t2.periodstart and t1.periodend>t2.periodstart and t1.itemid=t2.itemid
union
select
t1.itemid,t1.periodid,dateAdd(d,1,t2.periodend), t1.periodend, t1.price
from t1
inner join t2 on t1.periodstart < t2.periodend and t1.periodend>t2.periodend and t1.itemid=t2.itemid
order by periodstart
Use a Union?
Select itemid, periodid,periodstart, periodend,price FROM table1
UNION
SELECT itemid, periodid,periodstart, periodend,price FROM table2
Are you trying to do some sort of join though? the result set doesn't match the two tables you supplied.
Are you accounting for entries that line up or are you just trying to combine the rows?
if the latter, you could just do a Union
Select itemid, periodid, periodstart, periodend, price
From Table1
Union
Select itemid, periodid, periodstart, periodend, price
From Table2

Dealing with periods and dates without using cursors

I would like to solve this issue avoiding to use cursors (FETCH).
Here comes the problem...
1st Table/quantity
------------------
periodid periodstart periodend quantity
1 2010/10/01 2010/10/15 5
2st Table/sold items
-----------------------
periodid periodstart periodend solditems
14343 2010/10/05 2010/10/06 2
Now I would like to get the following view or just query result
Table Table/stock
-----------------------
periodstart periodend itemsinstock
2010/10/01 2010/10/04 5
2010/10/05 2010/10/06 3
2010/10/07 2010/10/15 5
It seems impossible to solve this problem without using cursors, or without using single dates instead of periods.
I would appreciate any help.
Thanks
DECLARE #t1 TABLE (periodid INT,periodstart DATE,periodend DATE,quantity INT)
DECLARE #t2 TABLE (periodid INT,periodstart DATE,periodend DATE,solditems INT)
INSERT INTO #t1 VALUES(1,'2010-10-01T00:00:00.000','2010-10-15T00:00:00.000',5)
INSERT INTO #t2 VALUES(14343,'2010-10-05T00:00:00.000','2010-10-06T00:00:00.000',2)
DECLARE #D1 DATE
SELECT #D1 = MIN(P) FROM (SELECT MIN(periodstart) P FROM #t1
UNION ALL
SELECT MIN(periodstart) FROM #t2) D
DECLARE #D2 DATE
SELECT #D2 = MAX(P) FROM (SELECT MAX(periodend) P FROM #t1
UNION ALL
SELECT MAX(periodend) FROM #t2) D
;WITH
L0 AS (SELECT 1 AS c UNION ALL SELECT 1),
L1 AS (SELECT 1 AS c FROM L0 A CROSS JOIN L0 B),
L2 AS (SELECT 1 AS c FROM L1 A CROSS JOIN L1 B),
L3 AS (SELECT 1 AS c FROM L2 A CROSS JOIN L2 B),
L4 AS (SELECT 1 AS c FROM L3 A CROSS JOIN L3 B),
Nums AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS i FROM L4),
Dates AS(SELECT DATEADD(DAY,i-1,#D1) AS D FROM Nums where i <= 1+DATEDIFF(DAY,#D1,#D2)) ,
Stock As (
SELECT D ,t1.quantity - ISNULL(t2.solditems,0) AS itemsinstock
FROM Dates
LEFT OUTER JOIN #t1 t1 ON t1.periodend >= D and t1.periodstart <= D
LEFT OUTER JOIN #t2 t2 ON t2.periodend >= D and t2.periodstart <= D ),
NStock As (
select D,itemsinstock, ROW_NUMBER() over (order by D) - ROW_NUMBER() over (partition by itemsinstock order by D) AS G
from Stock)
SELECT MIN(D) AS periodstart, MAX(D) AS periodend, itemsinstock
FROM NStock
GROUP BY G, itemsinstock
ORDER BY periodstart
Hopefully a little easier to read than Martin's. I used different tables and sample data, hopefully extrapolating the right info:
CREATE TABLE [dbo].[Quantity](
[PeriodStart] [date] NOT NULL,
[PeriodEnd] [date] NOT NULL,
[Quantity] [int] NOT NULL
) ON [PRIMARY]
CREATE TABLE [dbo].[SoldItems](
[PeriodStart] [date] NOT NULL,
[PeriodEnd] [date] NOT NULL,
[SoldItems] [int] NOT NULL
) ON [PRIMARY]
INSERT INTO Quantity (PeriodStart,PeriodEnd,Quantity)
SELECT '20100101','20100115',5
INSERT INTO SoldItems (PeriodStart,PeriodEnd,SoldItems)
SELECT '20100105','20100107',2 union all
SELECT '20100106','20100108',1
The actual query is now:
;WITH Dates as (
select PeriodStart as DateVal from SoldItems union select PeriodEnd from SoldItems union select PeriodStart from Quantity union select PeriodEnd from Quantity
), Periods as (
select d1.DateVal as StartDate, d2.DateVal as EndDate
from Dates d1 inner join Dates d2 on d1.DateVal < d2.DateVal left join Dates d3 on d1.DateVal < d3.DateVal and d3.DateVal < d2.DateVal where d3.DateVal is null
), QuantitiesSold as (
select StartDate,EndDate,COALESCE(SUM(si.SoldItems),0) as Quantity
from Periods p left join SoldItems si on p.StartDate < si.PeriodEnd and si.PeriodStart < p.EndDate
group by StartDate,EndDate
)
select StartDate,EndDate,q.Quantity - qs.Quantity
from QuantitiesSold qs inner join Quantity q on qs.StartDate < q.PeriodEnd and q.PeriodStart < qs.EndDate
And the result is:
StartDate EndDate (No column name)
2010-01-01 2010-01-05 5
2010-01-05 2010-01-06 3
2010-01-06 2010-01-07 2
2010-01-07 2010-01-08 4
2010-01-08 2010-01-15 5
Explanation: I'm using three Common Table Expressions. The first (Dates) is gathering all of the dates that we're talking about, from the two tables involved. The second (Periods) selects consecutive values from the Dates CTE. And the third (QuantitiesSold) then finds items in the SoldItems table that overlap these periods, and adds their totals together. All that remains in the outer select is to subtract these quantities from the total quantity stored in the Quantity Table
John, what you could do is a WHILE loop. Declare and initialise 2 variables before your loop, one being the start date and the other being end date. Your loop would then look like this:
WHILE(#StartEnd <= #EndDate)
BEGIN
--processing goes here
SET #StartEnd = #StartEnd + 1
END
You would need to store your period definitions in another table, so you could retrieve those and output rows when required to a temporary table.
Let me know if you need any more detailed examples, or if I've got the wrong end of the stick!
Damien,
I am trying to fully understand your solution and test it on a large scale of data, but I receive following errors for your code.
Msg 102, Level 15, State 1, Line 20
Incorrect syntax near 'Dates'.
Msg 102, Level 15, State 1, Line 22
Incorrect syntax near ','.
Msg 102, Level 15, State 1, Line 25
Incorrect syntax near ','.
Damien,
Based on your solution I also wanted to get a neat display for StockItems without overlapping dates. How about this solution?
CREATE TABLE [dbo].[SoldItems](
[PeriodStart] [datetime] NOT NULL,
[PeriodEnd] [datetime] NOT NULL,
[SoldItems] [int] NOT NULL
) ON [PRIMARY]
INSERT INTO SoldItems (PeriodStart,PeriodEnd,SoldItems)
SELECT '20100105','20100106',2 union all
SELECT '20100105','20100108',3 union all
SELECT '20100115','20100116',1 union all
SELECT '20100101','20100120',10
;WITH Dates as (
select PeriodStart as DateVal from SoldItems
union
select PeriodEnd from SoldItems
union
select PeriodStart from Quantity
union
select PeriodEnd from Quantity
), Periods as (
select d1.DateVal as StartDate, d2.DateVal as EndDate
from Dates d1
inner join Dates d2 on d1.DateVal < d2.DateVal
left join Dates d3 on d1.DateVal < d3.DateVal and
d3.DateVal < d2.DateVal where d3.DateVal is null
), QuantitiesSold as (
select StartDate,EndDate,SUM(si.SoldItems) as Quantity
from Periods p left join SoldItems si on p.StartDate < si.PeriodEnd and si.PeriodStart < p.EndDate
group by StartDate,EndDate
)
select StartDate,EndDate, qs.Quantity
from QuantitiesSold qs
where qs.quantity is not null