SQL subquery with comparison - sql

On a Rails (5.2) app with PostgreSQL I have 2 tables: Item and ItemPrice where an item has many item_prices.
Table Item
id
name
1
poetry book
2
programming book
Table ItemPrice
id
item_id
price
1
1
4
2
2
20
3
1
8
4
1
6
5
2
22
I am trying to select all the items for which the last price (price of the last offer price attached to it) is smaller than the one before it
So in this example, my request should only return item 1 because 6 < 8, and not item 2 because 22 > 20
I tried various combinations of Active records and SQL subqueries that would allow me to compare the last price with the second to last price but failed so far.
ex Item.all.joins(:item_prices).where('EXISTS(SELECT price FROM item_prices ORDER BY ID DESC LIMIT 1 as last_price WHERE (SELECT price FROM item_prices ... can't work it out..

You can do it as follows using ROW_NUMBER and LAG:
LAG to get the previous row based on a condition
WITH ranked_items AS (
SELECT m.*,
ROW_NUMBER() OVER (PARTITION BY item_id ORDER BY id DESC) AS rn,
LAG(price,1) OVER (PARTITION BY item_id ORDER BY id ) previous_price
FROM ItemPrice AS m
)
SELECT it.*
FROM ranked_items itp
inner join Item it on it.id = itp.item_id
WHERE rn = 1 and price < previous_price
Demo here

Related

How to I stop duplication on SQL join where I have order_ids and when people order more than 1 item (so multiple product_ids) to calculate discounts?

So my problem is my discount number is blowing up because an order has a discount for the entire order, but I am making a dataset where there are multiple lines for each order to represent each product in the order. Instead of the discount only applying once to the order, it adds the discount for every line.
what is happening
order_id
product_id
quantity
amount
discount
1
a
1
5
0
2
a
1
5
7
2
b
1
10
7
3
a
1
5
5
3
b
1
10
5
3
c
1
15
5
what i want
order_id
product_id
quantity
amount
discount
1
a
1
5
0
2
a
1
5
7
2
b
1
10
0
3
a
1
5
5
3
b
1
10
0
3
c
1
15
0
I just want the discount to be applied once per order, and my join is using order_id so that is why the discount is applying multiple times. I would attach my code, but it's a decent sized CTE
Figured it out. I did need to use a row_number() Over Partition by Order id, but I was also losing records if the order had more than 1 item. The solution was to use a CASE WHEN statement.
CASE WHEN ORDER_ROW_COUNT = 1 THEN DISCOUNT ELSE 0 END
this allowed me to keep the records without duplicating the discounts
You’re joining on a field that isn’t unique so the join is returning all the records for that order Id and therefore the discount is being applied to all the records for that order Id. You need some sort of differentiator field. Something that is unique in each orders data set.
Example:
Select *, row_number () over(partition by order_id order by order_id) as rownumber into #temp from table
This should give you something like in the picture.
rownumber table image
Then join on order_Id = order_Id and rownumber =1 and this would only update the first record for each order.

How to combine some rows into a single row, and delete other rows?

I have a table like this:
id | invoice_id | product_id | quantity | total
1 5 10 2 100
2 5 10 1 50
3 5 11 1 200
4 5 11 1 200
I want to combine the rows having same product_id in an invoice by adding their quantities and total values to one of the rows and then delete the other rows in the table. So the output should be like this
id | invoice_id | product_id | quantity | total
1 5 10 3 150
3 5 11 2 400
How can I do this? I was thinking of using an sql function that returns a list of id's having the same invoice and product and then using aggregate functions on quantity and price. Are there any simpler ways to do this?
First, you need an UPDATE statement that updates for each invoice_id, product_id combination the row with the min id with the totals of quantity and total:
UPDATE tablename t
SET quantity = s.quantity,
total = s.total
FROM (
SELECT MIN(id) id, SUM(quantity) quantity, SUM(total) total
FROM tablename
GROUP BY invoice_id, product_id
) s
WHERE s.id = t.id;
Then a DELETE statement to delete all the other ids:
DELETE FROM tablename t1
WHERE t1.id > (
SELECT MIN(t2.id)
FROM tablename t2
WHERE t2.invoice_id = t1.invoice_id AND t2.product_id = t1.product_id
);
See the demo.
This looks like an aggregation query:
select min(id) as id, invoice_id, product_id,
sum(quantity) as quantity, sum(total) as total
from t
group by invoice_id, product_id;

identify the week second purchase was recorded for each customer ID

please help me in SQL
I want to findout weekno the second purchase was made for each customer ID
here purchaseyn column value 1 means purchase made and 0 means not made
Table customerinfo
Week_No customerID PurcahseYn
201643 1 0
201643 2 1
201644 1 1
201644 2 1
201645 1 1
I want output like
Weekno CustomerID
201645 1
201644 2
Many thanks
You didn't state your DBMS so the following is standard SQL:
select week_no, customer_id
from (
select week_no, customer_id,
row_number() over (partition by customer_id order by week_no) as rn
from customerinfo
where purchaseyn = 1
) t
where rn = 2;
The above uses a window function number the purchases done by each customer and then restricts the overall result to the second one.

SQL query to fetch OrderID, transactionID, Status based on transaction status which is Char

I have below tables where I want to get lowest transaction entry based on Status which is Char.
Table1 (Order):
OrderID Product
------------------
1 A
2 B
3 A
Table2 (Transaction):
OrderID TransactionID Status
---------------------------------
1 1 LOW
1 2 HIGH
1 3 MID
2 4 MID
2 5 HIGH
3 6 LOW
How can I get transaction with the lowest status
OrderID Status
-----------------
1 LOW
2 MID
3 LOW
One method uses row_number():
select t.*
from (select t.*,
row_number() over (partition by orderid
order by instr('LOW,MEDIUM,HIGH', status) as seqnum
from transaction t
) t
where seqnum = 1;
instr() is just a convenient way to assign an ordering to strings. It returns the position of the status in the first argument, which is convenient for sorting purposes in this case.

Find the Product Number

I am trying to find the product number/product name based on the following set of conditions:
Select top 1 productnumber in xyz table
where product number in (1,2,3) order by filingdate --only if the last filing date has this product number
If product number in (4,5,6) for the last filing date nothing should be selected
If product number not in (4,5,6) for the last filing, then select the next top 1 productnumber
where prodcutnumber in (1,2,3) order by filingdate
how can i achieve this in a single query, i tried case statement bu i am stuck with it.
Sample data:
pnumber fnumber fdate
1 1 12/31
2 1 12/10
1 2 12/10
4 2 12/11
5 2 12/12
7 3 12/12
1 3 12/11
the results should be
pnumber fnumber fdate
1 1 12/31
1 2 12/10
1 3 12/11
Try by giving datatype as varchar for fdate.
SELECT id,
funum,
fdate
FROM (SELECT Row_number()
OVER(
partition BY funum
ORDER BY id) rn,
*
FROM t)p
WHERE rn = 1