How to find the changed value in history table sql - sql

I want to find if the value changed for a particular ID
ID
PAYMENT METHOD
1
CASH
1
VISA
1
CASH
2
CASH
2
CASH
2
CASH
3
CHEQUE
3
VISA
3
VISA
4
CASH
4
CASH
4
CASH
For example, ID 1 and ID 3 changed their payment method and I want to write a query that can detect that change, thanks in advance
example for expected output:
ID
PAYMENT METHOD HAS CHANGED
1
VISA
3
CHEQUE

You can use:
SELECT id,
MIN(payment_method) KEEP (DENSE_RANK FIRST ORDER BY ROWNUM)
AS payment_method
FROM table_name
GROUP BY id
HAVING COUNT(DISTINCT payment_method) > 1
or:
SELECT *
FROM (
SELECT id,
payment_method,
ROWNUM AS ordering_column
FROM table_name t
)
MATCH_RECOGNIZE(
PARTITION BY id
ORDER BY ordering_column
MEASURES
FIRST(payment_method) AS payment_method
PATTERN (^ same+ diff)
DEFINE
same AS FIRST(payment_method) = payment_method,
diff AS diff.payment_method <> same.payment_method
);
(Note: ROWNUM should be replaced by a column that will identify a deterministic ordering in the rows for each ID such as a timestamp column; however, such a column is not present in your sample data so it cannot be used. ROWNUM will just number the rows in the order that the SQL engine processes them and that processing order may be non-deterministic.)
Which both output:
ID
PAYMENT_METHOD
1
CASH
3
CHEQUE
db<>fiddle here

Related

SQL subquery with comparison

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

Getting count of last records of 2 columns SQL

I was looking for a solution for the below mentioned scenario.
So my table structure is like this ; Table name : energy_readings
equipment_id
meter_id
readings
reading_date
1
1
100
01/01/2022
1
1
200
02/01/2022
1
1
null
03/01/2022
1
2
100
01/01/2022
1
2
null
04/01/2022
2
1
null
04/01/2022
2
1
399
05/01/2022
2
2
null
02/01/2022
So from this , I want to get the number of nulls for the last record of same equipment_id and meter_id. (Should only consider the nulls of the last record of same equipment_id and meter_id)
EX : Here , the last reading for equipment 1 and meter 1 is a null , therefore it should be considered for the count. Also the last reading(Latest Date) for equipment 1 and meter 2 is a null , should be considered for count. But even though equipment 2 and meter 1 has a null , it is not the last record (Latest Date) , therefore should not be considered for the count.
Thus , this should be the result ;
equipment_id
Count
1
2
2
1
Hope I was clear with the question.
Thank you!
You can use CTE like below. CTE LatestRecord will get latest record for equipment_id & meter_id. Later you can join it with your current table and use WHERE to filter out record with null values only.
;WITH LatestRecord AS (
SELECT equipment_id, meter_id, MAX(reading_date) AS reading_date
FROM energy_readings
GROUP BY equipment_id, meter_id
)
SELECT er.meter_id, COUNT(1) AS [Count]
FROM energy_readings er
JOIN LatestRecord lr
ON lr.equipment_id = er.equipment_id
AND lr.meter_id = er.meter_id
AND lr.reading_date = er.reading_date
WHERE er.readings IS NULL
GROUP BY er.meter_id
with records as(
select equ_id,meter_id,reading_date,readings,
RANK() OVER(PARTITION BY meter_id,equ_id
order by reading_date) Count
from equipment order by equ_id
)
select equ_id,count(counter)
from
(
select equ_id,meter_id,reading_date,readings,MAX(Count) as counter
from records
group by meter_id,equ_id
order by equ_id
) where readings IS NULL group by equ_id
Explanation:-
records will order data by reading_date and will give counting as 1,2,3..
select max of count from records
select count of counter where reading is null
Partition by will give counting as shown in image
Result

How to split column into two columns based on unique ID?

I have two IDs and I want to split those values into two different columns
Here is the example
I want to have one column name Cash for cash values and one column for Card values
id type value
1 cash 1000
2 card 3500
2 card 1600
1 cash 500
1 cash 300
Don't have any query because I don't know where to start from.
Expected results should be
id card cash
2 3500 null
1 null 1000
2 1600 null
1 null 500
1 null 300
Just use case expressions:
select id,
(case when type = 'card' then value end) as card,
(case when type = 'cash' then value end) as cash
from t;
Note: The ordering for the result set is indeterminate. You have not explained if the ordering is important to the question, but you seem to have the highest value for each id, then the second highest, and so on. If that is really desired:
order by row_number() over (partition by id order by value desc),
id desc

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.