Unpivot or transform data using MS Access Query - sql

Good afternoon guru's!
I'm hoping someone with Access & SQL experience (and time) would be able to write some SQL code that I could use in MS Access to transform/unpivot some data. I guess it's a bit like a an unpivot.
Current table "tbl_Weekly_Forecast" is structured like this;
Item | A | B | C | D |
-------------------------------
61000224 | 1 | 2 | 3 | 4 |
64000123 | 25 | 25 | 25 | 25 |
66400005 | 38 | 38 | 37 | 37 |
Desired query output is like this;
Item | Period | Forecast |
-------------------------------
61000224 | A | 1 |
61000224 | B | 2 |
61000224 | C | 3 |
61000224 | D | 4 |
64000123 | A | 25 |
64000123 | B | 25 |
64000123 | C | 25 |
64000123 | D | 25 |
66400005 | A | 38 |
66400005 | B | 38 |
66400005 | C | 37 |
66400005 | D | 37 |
There are actually 24 columns to be unpivoted in table "tbl_Weekly_Forecast", so I'm hoping I'll be able to expand or add in the column names in to the SQL code and expand it myself.
I have spent about a day trying advice from others who have asked similar questions here, but unfortunately have very little SQL experience and have failed miserably so far. Any help would be most appreciated!
Thank you!

You can simply use UNION ALL as follows:
SELECT ITEM, 'A' PERIOD, A AS FORECAST FROM YOUR_TABLE
UNION ALL
SELECT ITEM, 'B' PERIOD, B AS FORECAST FROM YOUR_TABLE
UNION ALL
SELECT ITEM, 'C' PERIOD, C AS FORECAST FROM YOUR_TABLE
UNION ALL
SELECT ITEM, 'D' PERIOD, D AS FORECAST FROM YOUR_TABLE

Related

SQL Query - How to get count where current row values in between previous rows

So, I have to calculate total Type A where From & To in between From & To Type B based on ID.
I can't describe it in good words, so this is the example and my expected result (Column Count) :
ID | Type | From | To | Count
-------------------------------
100 | A | 10 | 14 |
100 | A | 16 | 18 |
100 | B | 12 | 14 | 1
100 | B | 11 | 13 | 1
100 | B | 17 | 18 | 1
120 | A | 5 | 10 |
120 | A | 12 | 14 |
120 | A | 18 | 20 |
120 | A | 18 | 20 |
120 | A | 22 | 24 |
120 | B | 30 | 32 | 0
120 | B | 19 | 20 | 2
120 | B | 10 | 14 | 1
Anybody can help ? I'm expecting something similar like COUNT OVER or RANK OVER without GROUP BY because the table above is not original table, its from another subquery..
Hmmmm . . . I think a correlated subquery does what you want:
select t.*,
(case when type = 'B'
then (select count(*)
from t t2
where t2.type = 'A' and
t2.from >= t.to and
t2.to <= t.from
)
end) as count
from t;
I'm not sure if the logic for between includes the endpoints. Traditionally, in SQL, it does. But if that is not your intention, then you may need < or >.

group by case condition followed by union of two columns

I have the following columns in table Sales :
Category1,priceA,priceB,Category2,costA,costB,type.(some items in category1 are same as category2)
sum(priceA), sum(priceB)
are to be grouped by category1,type.
sum(costA), sum(costB)
are to be grouped by category2,type.
I need the final output as
Union(category1,category2) as category3 ,sum(priceA)+sum(costA),sum(priceB)+sum(costB),type
to be grouped by UNION(category1+category2),type.
(sum(priceA)+sum(costA) would happen whenever items in category1 matches with category2 and same would be for sum(priceB)+sum(costB))
I tried to do it by
select category1,sum(priceA),sum(priceB),type group by category1,type
UNION ALL
select category2,sum(costA),sum(costB),type group by category2,type
Then following it up with another sum and group by. But I want to know how to do it without separately selecting and avoiding the union of basically 2 tables. Can I use group by followed by case statement here? Actually the table I referred as sales is an inner join of multiple tables , hence the motivation to not use select on it separately on two occasaions( in my actual case it would be union of 4 select queries on the table which makes the query look really big too). Plus I dont have permission to create procedure so no PL/SQL. Any fancy way for the above situation which will shorten the query and improve the performance ?
EDIT- SAMPLE DATA (Category1,PriceA,PriceB,Category2,CostA,CostB,Type)
+-----+----+----+-----+----+----+---+
| AUS | 20 | 25 | UK | 35 | 40 | X |
| UK | 30 | 26 | SA | 32 | 40 | Y |
| USA | 22 | 24 | NZ | 38 | 36 | Z |
| BRA | 16 | 10 | USA | 25 | 25 | Z |
| RUS | 20 | 15 | UK | 20 | 30 | X |
+-----+----+----+-----+----+----+---+
Which I divided into union of two tables as these:
+-----+----+----+---+
| AUS | 20 | 25 | X |
| UK | 30 | 26 | Y |
| USA | 22 | 24 | Z |
| BRA | 16 | 10 | Z |
| RUS | 20 | 15 | X |
+-----+----+----+---+
And
+-----+----+----+---+
| UK | 55 | 70 | X |
| SA | 33 | 40 | Y |
| NZ | 38 | 36 | Z |
| USA | 25 | 25 | Z |
+-----+----+----+---+
Final output would be like :
+-----+----+----+---+
| UK | 55 | 70 | X |
| UK | 30 | 26 | Y |
| NZ | 38 | 36 | Z |
| USA | 47 | 49 | Z |
| AUS | 20 | 25 | X |
| SA | 32 | 40 | Y |
| BRA | 16 | 10 | Z |
| RUS | 20 | 15 | X |
+-----+----+----+---+
This will give you what you want. SQLFiddle.
with sample_data1 as (
select "Category1", "PriceA", "PriceB", "Type"
from sample_data
union all
select "Category2", "CostA", "CostB", "Type"
from sample_data
)
select "Category1", sum("PriceA"), sum("PriceB")
from sample_data1 sd1
group by "Category1", "Type"
You will have to have a union at some point, because you need to increase the number of rows from your original source table. You can't do it with just a CASE.

output difference of two values same column to another column

Can anhone help me out or point me in the right direction? What is simplest way to get from current table to output table??
Current Table
ID | type | amount |
2 | A | 19 |
2 | B | 6 |
3 | A | 5 |
3 | B | 11 |
4 | A | 1 |
4 | B | 23 |
Desires output
ID | type | amount | change |
2 | A | 19 | 13 |
2 | B | 6 | -6 |
3 | A | 5 | -22 |
3 | B | 11 | |
4 | A | 1 | |
4 | B | 23 | |
I don't get how the values are put on rows. You can, for instance, subtract the "B" value from the "A" value for any given id. For instance:
select t.*,
(case when type = 'A'
then amount - max(amount) filter (type = 'B') over (partition by id)
end) as diff_a_b
from t;

SQL efficient complex filtering when id are in a list

Sorry, I'm very novice in SQL and the title is probably not very clear, it's probably easier to explain with an example.
I have a table (see below) containing all the food item of a series of lunches.
---------------------------
| id | lunch_id | food_id |
---------------------------
| 1 | 11 | 21 |
| 2 | 11 | 22 |
| 3 | 11 | 23 |
| 4 | 12 | 21 |
| 5 | 12 | 24 |
| 6 | 12 | 25 |
| 7 | 13 | 21 |
| 8 | 13 | 23 |
| 9 | 13 | 26 |
I want to select all the lunch_id which contains a list of specific food items.
E.g. I want all the lunch_id which contains the food_id 21 AND 23, in this example the result is 11 and 13.
I can do it with a series of SQL statements, but I suspect there's a better way to do it.
Group by the lunch_id and take only those groups having both food_ids.
select lunch_id
from your_table
where food_id in (21,23)
group by lunch_id
having count(distinct food_id) = 2
you can use exists
select t1.* from table_name t1
where exists ( select 1 from table_name t2 where t1.lunch_id =t2.lunch_id
and t2.food_id in (21,23
having count(*)=2
)

find other columns value based on maximum of one column using groupby particular column

I have data like below
+-------+---------+--------+
| Count | Mindif | Device |
+-------+---------+--------+
| 45 | 3 | A |
| 78 | 4 | A |
| 52 | 5 | A |
| 24 | 6 | A |
| 22 | 1 | B |
| 22 | 2 | B |
| 34 | 3 | B |
| 37 | 4 | B |
| 52 | 5 | B |
| 34 | 6 | B |
| 13 | 1 | C |
| 30 | 2 | C |
| 57 | 3 | C |
| 111 | 4 | C |
| 35 | 5 | C |
+-------+---------+--------+
Want to find Mindif and device based on max value of count.
Output be like
+-------+---------+--------+
| Count | Mindif | Device |
+-------+---------+--------+
| 78 | 4 | A |
| 52 | 5 | B |
| 111 | 4 | C |
+-------+---------+--------+
You can use a query like this:
SELECT t1.Count, t1.Mindif, t1.Device
FROM mytable AS t1
JOIN (
SELECT Device, MAX(Count) AS Count
FROM mytable
GROUP BY Device
) AS t2 ON t1.Device = t2.Device AND t1.Count = t2.Count
The query uses a derived table that returns the max Count value per Device. Joining back to the original table we can get the desired result.
using Window Function
SELECT Count, Mindif, Device
FROM
(SELECT Count, Mindif, Device,
rank() over (order by Count desc) as r
FROM table) S
WHERE S.r = 1;
OR
Simple Join with MAX
SELECT a.* FROM table a
LEFT SEMI JOIN
(SELECT MAX(Count)Cnt
FROM table)b on (a.Count = b.Cnt)