PostgreSQL crosstab transpose row to column - sql

Hi guys im trying to transpose a row query result to 1 row per columns
1st query is select * from sku;
the result is :
id sku sku_code travel_code
1 sku1 1 1
2 sku1 2 2
3 sku1 3 3
4 sku1 4 4
5 sku1 5 5
6 sku1 6 6
7 sku1 7 7
8 sku2 8 8
9 sku2 9 9
10 sku2 10 10
11 sku2 11 11
12 sku2 12 12
13 sku2 13 13
14 sku2 14 14
15 sku3 15 15
16 sku3 16 16
17 sku3 17 17
18 sku3 18 18
19 sku3 19 19
20 sku3 20 21
21 sku3 21 21
expected output is
SKU SKU_CODE TRAVEL_CODE
sku1 1 2 3 4 5 6 7 1 2 3 4 5 6 7
sku2 8 9 10 11 12 13 14 8 9 10 11 12 13 14
sku3 15 16 17 18 19 20 21 15 16 17 18 19 20 21
no header is ok

You can use grouping with min and max aggregation functions
with tab(sku, sku_cod, travel_code) as
(
select 'sku1',109,'01' union all
select 'sku2',209,'02' union all
select 'sku2',309,'03' union all
select 'sku1',409,'04'
)
select sku, min(sku_cod) as sku_cod1, max(sku_cod) as sku_cod2,
min(travel_code) as travel_code1, max(travel_code) as travel_code2
from tab
group by sku
order by sku;
sku sku_cod1 sku_cod2 travel_code1 travel_code2
sku1 109 409 01 04
sku2 209 309 02 03

You are using Postgres, so I would recommend storing the values as arrays:
select sku, array_agg(sku_cod) as sku_cods
array_agg(travel_code) as travel_codes
from t
group by sku
order by sku;
This will allow you to handle more than two values.

Related

sum every 7 rows from column sales while ints representing n days away from installation of promotion-material (before and after the installation)

2 Stores, each with its sales data per day. Both get equipped with promotion material but not at the same day. After the pr_day the promotion material will stay there. Meaning, there should be a sales boost from the day of the installation of the promotion material.
Installation Date:
Store A - 05/15/2019
Store B - 05/17/2019
To see if the promotion was a success we measure the sales before the pr-date and after by returning number of sales (not revenue but pieces sold) next to the int, indicating how far away it was from the pr-day: (sum of sales from both stores)
pr_date| sales
-28 | 35
-27 | 40
-26 | 21
-25 | 36
-24 | 29
-23 | 36
-22 | 43
-21 | 31
-20 | 32
-19 | 21
-18 | 17
-17 | 34
-16 | 34
-15 | 37
-14 | 32
-13 | 29
-12 | 25
-11 | 45
-10 | 43
-9 | 26
-8 | 27
-7 | 33
-6 | 36
-5 | 17
-4 | 34
-3 | 33
-2 | 21
-1 | 28
1 | 16
2 | 6
3 | 16
4 | 29
5 | 32
6 | 30
7 | 30
8 | 30
9 | 17
10 | 12
11 | 35
12 | 30
13 | 15
14 | 28
15 | 14
16 | 16
17 | 13
18 | 27
19 | 22
20 | 34
21 | 33
22 | 22
23 | 13
24 | 35
25 | 28
26 | 19
27 | 17
28 | 29
you may noticed, that i already removed the day from the installation of the promotion material.
The issue starts with the different installation date of the pr-material. If I group by weekday it will combine the sales from different days away from the installation. It will just start at whatever weekday i define:
Select DATEDIFF(wk, change_date, sales_date), sum(sales)
from tbl_sales
group by DATEDIFF(wk, change_date, sales_date)
result:
week | sales
-4 | 75
-3 | 228
-2 | 204
-1 | 235
0 | 149
1 | 173
2 | 151
3 | 167
4 | 141
the numbers are not from the right days and there is one week to many. Guess this is comming from sql grouping the sales starting from Sunday and because the pr_dates are different it generates more than just the 8 weeks (4 before, 4 after)
trying to find a sustainable solution i couldn't find the right fit and decided to post it here. Very thankfull for every thoughts of the community about this topics. Quite sure there is a smart solution for this problem cause it doesn't look like a rare request to me
I tried it with over as well but i don't see how to sum the 7 days together as they are not date days anymore but delta to the pr-date
Desired Result:
week | sales
-4 | 240
-3 | 206
-2 | 227
-1 | 202
1 | 159
2 | 167
3 | 159
4 | 163
Attachment from my analysis by hand what the Results should be:
Why do i need the weekly summary -> the Stores are performing differently depending on the weekday. With summing 7 days together I make sure we don't compare mondays to sundays and so on. Furthermore, the result will be represented in a Line- or Barchart where you could see the weekday variation in a ugly way. Meaning it will be hard for your eyes to see the trend/devolopment of the salesnumbers. Whereas the weekly comparison will absorb this variations.
If anything is unclear please feel free to let me know so i could provide you with futher details
Thank you very much
Additional the different Installation date overview:
Shop A:
store A
delta date sales
-28 17.04.2019 20
-27 18.04.2019 20
-26 19.04.2019 13
-25 20.04.2019 25
-24 21.04.2019 16
-23 22.04.2019 20
-22 23.04.2019 26
-21 24.04.2019 15
-20 25.04.2019 20
-19 26.04.2019 13
-18 27.04.2019 13
-17 28.04.2019 20
-16 29.04.2019 21
-15 30.04.2019 20
-14 01.05.2019 17
-13 02.05.2019 13
-12 03.05.2019 9
-11 04.05.2019 34
-10 05.05.2019 28
-9 06.05.2019 19
-8 07.05.2019 14
-7 08.05.2019 23
-6 09.05.2019 18
-5 10.05.2019 9
-4 11.05.2019 22
-3 12.05.2019 17
-2 13.05.2019 14
-1 14.05.2019 19
0 15.05.2019 11
1 16.05.2019 0
2 17.05.2019 0
3 18.05.2019 1
4 19.05.2019 19
5 20.05.2019 18
6 21.05.2019 14
7 22.05.2019 11
8 23.05.2019 12
9 24.05.2019 8
10 25.05.2019 7
11 26.05.2019 19
12 27.05.2019 15
13 28.05.2019 15
14 29.05.2019 11
15 30.05.2019 5
16 31.05.2019 8
17 01.06.2019 10
18 02.06.2019 19
19 03.06.2019 14
20 04.06.2019 21
21 05.06.2019 22
22 06.06.2019 7
23 07.06.2019 6
24 08.06.2019 23
25 09.06.2019 17
26 10.06.2019 9
27 11.06.2019 8
28 12.06.2019 23
Shop B:
store B
delta date sales
-28 19.04.2019 15
-27 20.04.2019 20
-26 21.04.2019 8
-25 22.04.2019 11
-24 23.04.2019 13
-23 24.04.2019 16
-22 25.04.2019 17
-21 26.04.2019 16
-20 27.04.2019 12
-19 28.04.2019 8
-18 29.04.2019 4
-17 30.04.2019 14
-16 01.05.2019 13
-15 02.05.2019 17
-14 03.05.2019 15
-13 04.05.2019 16
-12 05.05.2019 16
-11 06.05.2019 11
-10 07.05.2019 15
-9 08.05.2019 7
-8 09.05.2019 13
-7 10.05.2019 10
-6 11.05.2019 18
-5 12.05.2019 8
-4 13.05.2019 12
-3 14.05.2019 16
-2 15.05.2019 7
-1 16.05.2019 9
0 17.05.2019 9
1 18.05.2019 16
2 19.05.2019 6
3 20.05.2019 15
4 21.05.2019 10
5 22.05.2019 14
6 23.05.2019 16
7 24.05.2019 19
8 25.05.2019 18
9 26.05.2019 9
10 27.05.2019 5
11 28.05.2019 16
12 29.05.2019 15
13 30.05.2019 17
14 31.05.2019 9
15 01.06.2019 8
16 02.06.2019 3
17 03.06.2019 8
18 04.06.2019 8
19 05.06.2019 13
20 06.06.2019 11
21 07.06.2019 15
22 08.06.2019 7
23 09.06.2019 12
24 10.06.2019 11
25 11.06.2019 10
26 12.06.2019 9
27 13.06.2019 6
28 14.06.2019 9
Try
select wk, sum(sales)
from (
select
isnull(sa.sales,0) + isnull(sb.sales,0) sales
, isnull(sa.delta , sb.delta) delta
, case when isnull(sa.delta , sb.delta) = 0 then 0
else case when isnull(sa.delta , sb.delta) > 0 then (isnull(sa.delta , sb.delta) -1) /7 +1
else (isnull(sa.delta , sb.delta) +1) /7 -1
end
end wk
from shopA sa
full join shopB sb on sa.delta=sb.delta
) t
group by wk;
sql fiddle
A more readable version, it doesn't run faster, just using CROSS APLLY this way allows to indroduce sort of intermediate variables for cleaner code.
select wk, sum(sales)
from (
select
isnull(sa.sales,0) + isnull(sb.sales,0) sales
, dlt delta
, case when dlt = 0 then 0
else case when dlt > 0 then (dlt - 1) / 7 + 1
else (dlt + 1) / 7 - 1
end
end wk
from shopA sa
full join shopB sb on sa.delta=sb.delta
cross apply (
select dlt = isnull(sa.delta, sb.delta)
) tmp
) t
group by wk;
Finally, if you already have a query which produces a dataset with the (pr_date, sales) columns
select wk, sum(sales)
from (
select sales
, case when pr_date = 0 then 0
else case when pr_date > 0 then (pr_date - 1) / 7 + 1
else (pr_date + 1) / 7 - 1
end
end wk
from (
-- ... you query here ...
)pr_date_sales
) t
group by wk;
I think you just need to take the day difference and use arithmetic. Using datediff() with week counts week-boundaries -- which is not what you want. That is, it normalizes the weeks to calendar weeks.
You want to leave out the day of the promotion, which makes this a wee bit more complicated.
I think this is the logic:
Select v.week_diff, sum(sales)
from tbl_sales s cross join
(values (case when change_date < sales_date
then (datediff(day, change_date, sales_date) + 1) / 7
else (datediff(day, change_date, sales_date) - 1) / 7
end)
) v(week_diff)
where change_date <> sales_date
group by v.week_diff;
There might be an off-by-one problem, depending on what you really want to do when the dates are the same.

Return running max column from another column SQL

I have a query where I would like to manipulate a column to return an array conatining only the max-value from the last 12 values from another column (due to other parts of the query).
Example:
I want to add the column MaxLast12 from:
Month Power
1 10
2 16
3 8
4 14
5 15
6 3
7 6
8 10
9 11
10 12
11 12
12 12
13 18
14 12
To become:
Month Power MaxLast12
1 10 10
2 16 16
3 8 16
4 14 16
5 15 16
6 3 16
7 6 16
8 17 17
9 11 17
10 12 17
11 12 17
12 12 17
13 18 18
14 12 18
It would also help to be able to create a simpler solution where I only include the 12 rows in the query (won't be as accurate but good enough for the purpose) with only the maximum value. Would need to do the following:
Month Power
1 6
2 6
3 8
4 14
5 15
6 3
7 6
8 10
9 11
10 12
11 12
12 12
To become:
Month Power YearMax
1 10 17
2 16 17
3 8 17
4 14 17
5 15 17
6 3 17
7 6 17
8 17 17
9 11 17
10 12 17
11 12 17
12 12 17
Since I'm guessing both problems solution will be similar, any help possible is appriciated. Would like to avoid usign GROUP BY clause since I'm modifying an existing kind of complex query.
Tried to achive this using max() with no luck.
I am using SQL-developer.
In Oracle you would use window functions:
select month, power,
max(power) over (order by month rows between 11 preceding and current row)
from t;

Compare Current Row with Previous/Next row in SQL Server

I have a table named team and it like below: I just added a row_number in the 3rd column
RaidNo OutComeID RN
2 15 1
4 15 2
6 14 3
8 16 4
10 16 5
12 14 6
14 16 7
16 15 8
18 15 9
20 16 10
22 12 11
24 16 12
26 16 13
28 16 14
30 15 15
32 14 16
34 13 17
When the OutcomeId came as 16 then start with one and 16 comes consecutively, add one by one. And the results be like
RaidNo OutComeID RN Result
2 15 1 0
4 15 2 0
6 14 3 0
8 16 4 1
10 16 5 2
12 14 6 0
14 16 7 1
16 15 8 0
18 15 9 0
20 16 10 1
22 12 11 0
24 16 12 1
26 16 13 2
28 16 14 3
30 15 15 0
32 14 16 0
34 13 17 0
Help me to get the result.
You can use the following query:
SELECT RaidNo, OutComeID, RN,
CASE
WHEN OutComeID <> 16 THEN 0
ELSE ROW_NUMBER() OVER (PARTITION BY OutComeID, grp ORDER BY RN)
END AS Result
FROM (
SELECT RaidNo, OutComeID, RN,
RN - ROW_NUMBER() OVER (PARTITION BY OutComeID ORDER BY RN) AS grp
FROM mytable) AS t
ORDER BY RN
Field grp identifies slices (also called islands) of consecutive records having the same OutComeID value. The outer query uses grp in order to enumerate each record that belongs to a '16' slice. The records that belong to the other slices are assigned value 0.
Demo here

Select sum of condition

I have a table like below. I want to select some rows that sum of cnt<120. how to do this?
cnt id
_________
6 14001
17 14005
14 14017
16 14024
9 14025
7 14027
10 14029
14 14048
23 14055
18 14056
19 14058
18 14059
18 14063
15 14064
9 14086
17 14095
9 14098
14 14116
10 14138
8 14147
17 14165
22 14171
22 14191
18 14194
13 14204
17 14221
13 14245
14 14249
6 14254
17 14257
9 14260
19 14261
26 14263
6 14264
27 14265
19 14269
11 14287
SELECT SUM(cnt)
FROM my_table
WHERE cnt < 120
You can try this:-
SELECT *
FROM test O
HAVING (SELECT sum(cnt) FROM test WHERE cnt <= O.cnt) <120

select last entry for every user multi table example

Given the following tables
table message
id message time_send
14 "first" 2014-02-10 22:16:31
15 "second" 2014-02-14 09:35:20
16 "third" 2014-02-13 09:35:47
17 "fourth" 2014-03-10 22:16:31
18 "fifth" 2014-03-14 09:35:20
19 "sixth" 2014-04-12 09:35:47
20 "seventh" 2014-04-13 09:35:47
21 "eighth" 2014-04-14 09:35:47
table message_owner
id message_id owner_id cp_id
1 14 1 4
2 14 4 1
3 15 12 4
4 15 4 12
5 16 4 1
6 16 1 4
7 17 12 4
8 17 4 12
9 18 4 1
10 18 1 4
11 19 12 4
12 19 4 12
13 20 12 1
14 20 1 12
15 21 12 7
16 21 7 12
I want to query the most recent message with every counter party(cp_id) for a given owner.
For example for owner_id=4 I would like the following output:
id message time_send owner_id cp_id
18 "fifth" 2014-03-14 09:35:20 4 1
19 "sixth" 2014-02-13 09:35:47 4 12
I see a lot of examples with one table but I am not able to transpose them in a multitable example.
edit1: adding more entries
This should work:
SELECT m.id, m.message, mo.owner_id, mo.cp_id
FROM message m
JOIN message_owner mo ON m.id=mo.message_id
WHERE mo.owner_id=4
AND m.time_send=(
SELECT MAX(time_send)
FROM message m2
JOIN message_owner mo2 ON mo2.message_id=m2.id
WHERE mo2.owner_id=mo.owner_id
AND mo2.cp_id =mo.cp_id
)
... notice though that putting a WHERE condition on a timestamp column can sometimes not work correctly.
Same example without jointures:
SELECT m.id, m.time_send, m.message, mo.owner_id, mo.cp_id
FROM message m, message_owner mo
WHERE m.id = mo.message_id
AND mo.owner_id = 4
AND m.time_send = (
SELECT MAX(time_send)
FROM message m2, message_owner mo2
WHERE mo2.message_id = m2.id
AND mo2.owner_id = mo.owner_id
AND mo2.cp_id = mo.cp_id
);
http://sqlfiddle.com/#!2/558d7/4