AWS Athena create rows based on a column value - sql

I have a table showing simple order details as follows
+---------+---------+---------+
| Order_id| Item_id |Quantity |
+---------+---------+---------+
| 10001 | 1 | 4 |
+---------+---------+---------+
Now I need to show each quantity as a line item as follows.
+---------+---------+---------+---------+
| Order_id| Item_id | Line_id |Quantity |
+---------+---------+---------+---------+
| 10001 | 1 | 1 | 1 |
+---------+---------+---------+---------+
| 10001 | 1 | 2 | 1 |
+---------+---------+---------+---------+
| 10001 | 1 | 3 | 1 |
+---------+---------+---------+---------+
| 10001 | 1 | 4 | 1 |
+---------+---------+---------+---------+
Can any one help me how can I write query to achieve this?

We can try using a recursive CTE here:
WITH cte AS (Order_id, Item_id, Line_id, Amount) AS (
SELECT Order_id, Item_id, Quantity, 1
FROM yourTable
UNION ALL
SELECT Order_id, Item_id, Line_id - 1, 1
FROM cte
WHERE Line_id - 1 >= 1
)
SELECT Order_id, Item_id, Line_id, Quantity
FROM cte
ORDER BY Order_id, Item_id, Line_id;
The logic in the CTE above is that we form the base case of the recursion using the following tuple from your source table:
(10001, 1, 4, 1)
That is, we start counting for the Line_id from 4, decreasing by one in each step of the recursion. At the end of the recursive case, we hit a Line_id value of 1.

You can use unnest on an array to get the value you want:
select od.order_id, od.item_id, u.line_number, u.quantity
from order_details od cross join
unnest(repeat(1, od.quantity)) with ordinality u(quantity, line_number)

Related

SQL: SELECT all and Remove duplicated rows based on values from multiple columns

I have the following SQL table name 'orders':
+--------------+------------+-------------------+------------+
| order_id | item_id | amount | commission |
+--------------+------------+-------------------+------------+
| 111 | 1234 | 23 | 1 |
| 222 | 1234 | 34 | 2 |
| 111 | 2345 | 45 | 3 |
| 111 | 1234 | 23 | 1 |
+--------------+------------+-------------------+------------+
And I'm trying to Select only the rows that the order_id and item_id are NOT the same (remove duplicates only if BOTH has the same value), I tried using "Group_By" as follows:
SELECT * FROM orders GROUP BY order_id,item_id
but this, will remove all duplicates of order_id, and all duplicated of item_id, here is the result:
+--------------+------------+-------------------+------------+
| order_id | item_id | amount | commission |
+--------------+------------+-------------------+------------+
| 111 | 1234 | 23 | 1 |
| 222 | 1234 | 34 | 2 |
+--------------+------------+-------------------+------------+
I've tried using 'DISTINCT' too, but I need to select all columns in the result.
here is the expected result:
+--------------+------------+-------------------+------------+
| order_id | item_id | amount | commission |
+--------------+------------+-------------------+------------+
| 111 | 1234 | 23 | 1 |
| 222 | 1234 | 34 | 2 |
| 111 | 2345 | 45 | 3 |
+--------------+------------+-------------------+------------+
I hope its clear, Thank you.
You can use row_number():
select order_id, item_id, amount, commission
from (
select t.*, row_number() over(partition by order_id, item_id order by commission) rn
from mytable t
) t
where rn = 1
With your sample data, it is not easy to see exactly which partition and order by clause you are looking for, so you might need to adjust them to your exact use case.
Simply:
SELECT DISTINCT * FROM orders
SELECT DISTINCT should do what you want
SELECT DISTINCT order_id, item_id, amount, commission
FROM orders;
If you have more columns, but only care about these being duplicated, then you can use ROW_NUMBER():
SELECT o.*
FROM (SELECT o.*,
ROW_NUMBER() OVER (PARTITION BY order_id, item_id, amount, commission ORDER BY (SELECT NULL)) as seqnum
FROM orders o
) o
WHERE seqnum = 1;
You can try this.
create table MyTable
(order_id int
, item_id int
, amount int
, commission int)
insert into MyTable values
(111, 1234, 23, 1),
(222, 1234, 34, 2),
(111, 2345, 45, 3),
(111, 1234, 23, 1)
select distinct * from MyTable
Live db<>fiddle demo.

Compare Previous column data with the next column data

I have a sales table with the following columns. I want to select the rows where sale price is increasing and skip those decrease sale price in which the sale price of above row is increase.
e.g. in the following table, I would like to have all rows except row having saleid=4
+--------+--------+-----------+
| SaleId | ItemId | SalePrice |
+--------+--------+-----------+
| 1 | 987 | 12 |
+--------+--------+-----------+
| 2 | 678 | 13 |
+--------+--------+-----------+
| 3 | 987 | 15 |
+--------+--------+-----------+
| 4 | 542 | 11 |
+--------+--------+-----------+
| 5 | 678 | 16 |
+--------+--------+-----------+
I have tried using inner join. But it shows nothing.
Here is the query I have wrote:
select s1.* from saletable s1
join saletable s2 on s1.saleid = s2.saleid
where s1.saleprice<s2.saleprice
Consider the following solution using running max
select t.*
from
(
select *, max(SalePrice) over (order by SaleId) runningMaxSalePrice
from testdata
) t
where t.SalePrice >= t.runningMaxSalePrice
This solution skips more than one consecutive row with decreasing SalePrice.
DBFdiddle DEMO
Use lag():
select st.*
from (select st.*, lag(saleprice) over (order by saleid ) as prev_saleprice
from saletable st
) st
where prev_saleprice is null or saleprice > prev_saleprice

Sum across columns and rows

Consider a table like this
table
+--------+---------+-----------+---------+-----------+
| BookId | ItemId1 | Quantity1 | ItemId2 | Quantity2 |
+--------+---------+-----------+---------+-----------+
| 1 | 1 | 2 | 2 | 1 |
| 1 | 3 | 1 | 2 | 1 |
| 2 | 1 | 1 | 2 | 1 |
+--------+---------+-----------+---------+-----------+
Now I want to get the sum of columns quantity for each item grouped by book. How can I take the sum across different columns then? right now I use an awkward solution like building a temporary table and then querying this one, but it must be possible in a more elegant way!?
select
BookId 'BookId',
ItemId1 'ItemId',
Quantity1 'Quantity'
into #temptab
from table
union all
select
BookId,
ItemId2,
Quantity2
from table
and after that
select
BookId,
ItemId,
sum(Quantity)
from #temptab
group by ItemId, BookId
How can I get rid of this intermediate step?
Desired output:
+--------+--------+----------+
| BookId | ItemId | Quantity |
+--------+--------+----------+
| 1 | 1 | 2 |
| 1 | 3 | 1 |
| 1 | 2 | 2 |
| 2 | 1 | 1 |
| 2 | 2 | 1 |
+--------+--------+----------+
Use cross apply with table valued constructor to unpivot the data then find sum per bookid and item.
This will avoid your intermediate step
SELECT BookId,
item,
Sum(quantity)
FROM Youratble
CROSS apply (VALUES(Quantity1,ItemId1),
(Quantity2,ItemId2))cs(quantity, item)
GROUP BY BookId,
item
As mentioned by Mikael Eriksson for sql server 2005 use this
SELECT BookId,
item,
Sum(quantity)
FROM Youratble
cross apply
(select Quantity1, ItemId1
union all
select Quantity2, ItemId2) as cs(quantity, item)
GROUP BY BookId,
item
Based on what you've done, you can do it in one single step using subqueries:
select
combined.BookId,
combined.ItemId,
sum(combined.Quantity)
from
(select
BookId 'BookId',
ItemId1 'ItemId',
Quantity1 'Quantity'
from tableName
union all
select
BookId,
ItemId2,
Quantity2
from tableName) as combined
group by combined.ItemId, combined.BookId

Inconsistent Transpose

Given a table A has the following data:
+----------+-------+
| Supplier | buyer |
+----------+-------+
| A | 1 |
| A | 2 |
| B | 3 |
| B | 4 |
| B | 5 |
+----------+-------+
My question is, can I transpose the second column so the resultant table will be like:
+----------+--------+--------+--------+
| Supplier | buyer1 | buyer2 | buyer3 |
+----------+--------+--------+--------+
| A | 1 | 2 | |
| B | 3 | 4 | 5 |
+----------+--------+--------+--------+
Assuming the maximum number of buyers is known as three.
You could use a common table expression to give each buyer an order within the supplier, and then just do a regular case to put them in columns;
WITH cte AS (
SELECT supplier, buyer,
ROW_NUMBER() OVER (PARTITION BY supplier ORDER BY buyer) rn
FROM Table1
)
SELECT supplier,
MAX(CASE WHEN rn=1 THEN buyer END) buyer1,
MAX(CASE WHEN rn=2 THEN buyer END) buyer2,
MAX(CASE WHEN rn=3 THEN buyer END) buyer3
FROM cte
GROUP BY supplier;
An SQLfiddle to test with.
You may consider using PIVOT clause:
select *
from (
select supplier, buyer, row_number() over (partition by supplier order by buyer) as seq
from a
)
pivot (max(buyer) for seq in (1 as buyer1, 2 as buyer2, 3 as buyer3));
SQLFiddle here.

Count rows grouped by condition in SQL

We have a table like this:
+----+--------+
| Id | ItemId |
+----+--------+
| 1 | 1100 |
| 1 | 1101 |
| 1 | 1102 |
| 2 | 2001 |
| 2 | 2002 |
| 3 | 1101 |
+----+--------+
We want to count how many items each guy has, and show the guys with 2 items or more. Like this:
+----+-----------+
| Id | ItemCount |
+----+-----------+
| 1 | 3 |
| 2 | 2 |
+----+-----------+
We didn't count the guy with Id = 3 because he's got only 1 item.
How can we do this in SQL?
SELECT id, COUNT(itemId) AS ItemCount
FROM YourTable
GROUP BY id
HAVING COUNT(itemId) > 1
Use this query
SELECT *
FROM (
SELECT COUNT(ItemId ) AS COUNT, Id FROM ITEM
GROUP BY Id
)
my_select
WHERE COUNT>1
SELECT id,
count(1)
FROM YOUR_TABLE
GROUP BY id
HAVING count(1) > 1;
select Id, count(ItemId) as ItemCount
from table_name
group by Id
having ItemCount > 1