How to get just changed rows in mysql? - mysql

I have a table that has followed rows:
ID price rowNo
1 100 1
1 100 2
1 200 3
1 100 4
1 300 5
1 100 6
1 100 7
2 500 9
2 500 10
2 500 11
2 500 12
2 500 13
2 500 14
3 400 15
I want to get rows for each ID that the price has been changed. the output will be as follow:
ID price rowNo
1 100 1
1 200 3
1 100 4
1 300 5
1 100 6
2 500 9
3 400 15

You could use correlated sub queries in the where clause to test previous value or for existence
drop table if exists t;
create table t
(ID int, price int, rowNo int);
insert into t values
(1 , 100 , 1),
(1 , 100 , 2),
(1 , 200 , 3),
(1 , 100 , 4),
(1 , 300 , 5),
(1 , 100 , 6),
(1 , 100 , 7),
(2 , 500 , 9),
(2 , 500 , 10),
(2 , 500 , 11),
(2 , 500 , 12),
(2 , 500 , 13),
(2 , 500 , 14),
(3 , 400 , 15);
select t.*
from t
where t.price <> (select t1.price from t t1 where t1.id = t.id and t1.rowno < t.rowno order by t1.rowno desc limit 1) or
(select t1.price from t t1 where t1.id = t.id and t1.rowno < t.rowno order by t1.rowno desc limit 1) is null;
+------+-------+-------+
| ID | price | rowNo |
+------+-------+-------+
| 1 | 100 | 1 |
| 1 | 200 | 3 |
| 1 | 100 | 4 |
| 1 | 300 | 5 |
| 1 | 100 | 6 |
| 2 | 500 | 9 |
| 3 | 400 | 15 |
+------+-------+-------+
7 rows in set (0.003 sec)

**
All credits to user :#1000111
Mysql select row when column value changed
**
For older MySQL version that does not support window function:
SELECT id,price,rowNo
FROM ( SELECT *,
IF(#prevprice = YT.price, #rn := #rn + 1,
IF(#prevprice := YT.price, #rn := 1, #rn := 1)
) AS rn
FROM test_tbl YT
CROSS JOIN
(
SELECT #prevprice := -1, #rn := 1
) AS var
ORDER BY YT.id
) AS t
WHERE t.rn = 1
ORDER BY t.id
Demo: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=a0deed41b868781e7b7a84b69556769e
Result:
id price rowNo
1 100 1
1 200 3
1 100 4
1 300 5
1 100 6
2 500 9
3 400 15

Related

sql exclude rows based on first occurrence of data and conditions

I have created a dataset that has columns for 2 customers:
Cust_No Transaction_date amount credit_debit running_total row_num
1 5/27/2022 800 D -200 1
1 5/26/2022 300 D 600 2
1 5/22/2022 800 C 900 3
1 5/20/2022 100 C 100 4
9 5/16/2022 500 D -300 1
9 5/14/2022 300 D 200 2
9 5/6/2022 200 C 500 3
9 5/5/2022 500 D 300 4
9 5/2/2022 300 D 800 5
9 5/2/2022 500 C 1100 6
9 5/1/2022 500 C 600 7
9 5/1/2022 100 C 100 8
The result I am looking for is:
Cust_No Transaction_date amount credit_debit running_total row_num
1 5/27/2022 800 D -200 1
1 5/26/2022 300 D 600 2
1 5/22/2022 800 C 900 3
9 5/16/2022 500 D -300 1
9 5/14/2022 300 D 200 2
9 5/6/2022 200 C 500 3
9 5/5/2022 500 D 300 4
9 5/2/2022 300 D 800 5
9 5/2/2022 500 C 1100 6
I sorted the dataset based on latest transaction for each customer.
We note the latest transaction amount and search for first occurrence of same amount that was a credit (C) and exclude the rest of the rows after it.
In the example above: Customer 9 has lastest debit transaction of 500, so we look for most recent credit transaction of 500 and exclude all the rows after that for customer 9.
Progress Made so far:
calculated the running total using logic:
sum (case when credit_debit ='C' then amount else -1*amount end) over (partition by cust_no order by transaction_date desc ) as running_total
I also got the data using lead 1,2,3,4,5 but this is not efficient and I could have multiple rows before I find the first credit number with amount same as 1st row:
case when lead(amount, 1) over(partition by cust_no order by transaction_date desc) = amount then amount else null end as lead1
No sure which dbms this is for but it need a lateral join in postgres.
It searches for the most recent transaction identified when rn = 1, then it matches that amount to an earlier credit transaction of the same amount and using the rn of that row to form a boundary of row numbers to be returned:
with CTE as (
select
Cust_No, Transaction_date, amount, credit_debit, running_total
, row_number() over(partition by cust_no order by transaction_date DESC) as rn
from mytable
)
, RANGE as (
select *
from CTE
left join lateral (
select c.rn as ignore_after
from CTE as c
where CTE.Cust_No = c.Cust_No
and CTE.amount = c.amount
and c.credit_debit = 'C'
and CTE.rn = 1
order by c.rn ASC
limit 1
) oa on true
where CTE.rn = 1
)
select
CTE.*
from CTE
inner join RANGE on CTE.rn between RANGE.rn and RANGE.ignore_after
and CTE.cust_no = RANGE.cust_no
Cust_No | Transaction_date | amount | credit_debit | running_total | rn
------: | :--------------- | -----: | :----------- | ------------: | -:
1 | 2022-05-27 | 800 | D | -200 | 1
1 | 2022-05-26 | 300 | D | 600 | 2
1 | 2022-05-22 | 800 | C | 900 | 3
9 | 2022-05-16 | 500 | D | -300 | 1
9 | 2022-05-14 | 300 | D | 200 | 2
9 | 2022-05-06 | 200 | C | 500 | 3
9 | 2022-05-05 | 500 | D | 300 | 4
9 | 2022-05-02 | 300 | D | 800 | 5
9 | 2022-05-02 | 500 | C | 1100 | 6
for postgres see: db<>fiddle here
nb: for an "outer apply" example I have also used SQL Server in the following fiddle see: db<>fiddle here

Select limited number of rows based on unique column value

I have a table in the following format
ID SOURCE_ID
1 1
2 1
3 1
4 2
5 3
6 3
7 4
8 4
9 4
10 4
11 4
12 1
13 1
14 3
15 3
16 3
17 3
18 2
19 2
I want to be able to select 5 records MAX for each unique source_id.
So I should end up having returned 5 rows for source_id = 1, 5 rows for souce_id = 2, and so on.
Any ideas? Thank you in advance.
E.g.:
SELECT id
, source_id
FROM
( SELECT id
, source_id
, CASE WHEN #prev = source_id THEN #i:=#i+1 ELSE #i:=1 END i
, #prev := source_id prev
FROM my_table
, (SELECT #prev:=null,#i:=0) vars
ORDER
BY source_id
, id
) x
WHERE i <=5
ORDER
BY id;

mysql cumulative sum of same field value

I have sample data with table name catdog
| No | id | data |
1 1 4000
2 2 300
3 3 100
4 1 400
5 2 30
6 3 10
7 1 40
8 2 3
9 3 1
I want the result like this:
| No | id | data | totaldata |
1 1 4000 4000
2 2 300 300
3 3 100 100
4 1 400 4400 --------> 4000 + 400 on id
5 2 30 330 --------> 300 + 30 on id
6 3 10 110 --------> 100 + 10 on id
7 1 40 4440 --------> 4000 + 400 + 40 on id
8 2 3 333 --------> 300 + 30 + 1 on id
9 3 1 111 --------> 100 + 10 + 1 on id
Sum if field No is same.
How to write a mysql query for my case?
very very thank you so much GurV
Try this:
select no, id, data, c_data from (
select t1.*,
#data := case when #id = id then #data + data else data end c_data,
#id := id
from
(select * from catdog
order by id, No) t1
cross join (select #id := -1, #data := 0) t2) t
order by no;
It uses user defined variables to keep track of sum till now for each id

Get the latest record for each employee older than a given date

I have a status history table that also includes future dated records.
Example: employee_jobs
id | employee_id | division_id | department_id | job_id | effective_date
1 100 1 1 1 2015-01-01
2 100 1 1 2 2016-01-01
3 100 1 2 4 2017-01-01
4 200 1 3 5 2016-01-01
5 300 1 3 6 2015-01-01
6 300 1 3 7 2016-05-25
I need a preforming SQL that will show a given employee_id's current live record when given a date: Example Date = 2016-08-15
The result set should be:
id | employee_id | division_id | department_id | job_id | effective_date
2 100 1 1 2 2016-01-01
4 200 1 3 5 2016-01-01
6 300 1 3 7 2016-05-25
I guess you want records for each employee having the latest effective_date with a constraint
(effective_date must be less than or equal to a given date)
SELECT
*
FROM
(
SELECT
*,
IF(#sameEmployee = employee_id, #rn := #rn + 1,
IF(#sameEmployee := employee_id, #rn := 1, #rn := 1)
) AS row_number
FROM employee_jobs
CROSS JOIN (SELECT #sameEmployee := 0, #rn := 1) var
WHERE effective_date <= '2016-08-15'
ORDER BY employee_id, effective_date DESC
) AS t
WHERE t.row_number = 1
ORDER BY t.employee_id

MySQL SELECT BETWEEN two points

Table places
pid pname
1 Amsterdam
2 London
3 Miami
4 Bonn
5 Oslo
6 Madrid
7 Lisbon
Table roots
id from to projectid
2 1 3 1 //Project#1 starts from Amsterdam
3 3 2 1 //Project#1 ends at London
4 3 5 2 //Project#2 starts from Miami and ends at Oslo
5 3 5 3
6 5 6 3
7 4 2 4
8 2 4 5
9 6 4 6
10 4 5 6
I need a resultset that contains all start and end points, ie:
start end projectid
1 2 1 //Amsterdam London
3 5 2 //Miami Oslo
3 6 3
4 2 4
2 4 5
6 5 6
And so, I need a list will show all projects between two cities, for example between London and Bonn:
4 2 4
2 4 5
This assume the id are in sequential order
Using variables you assign a rowid to know what is the first and the last entry of each project.
JOIN both together choose the from from F and the to from T
SQL Fiddle Demo
SELECT F.`from`, T.`to`, F.`projectid`
FROM (
SELECT `id`, `from`, `to`, `projectid`,
#row := IF(#prev = `projectid`,
#row + 1,
IF( #prev := `projectid`, 1, 1)
) as rn
FROM roots R
CROSS JOIN (SELECT #row = 0 , #prev = 0) x
ORDER BY `projectid`, `id`
) F
JOIN
(
SELECT `id`, `from`, `to`, `projectid`,
#row := IF(#prev = `projectid`,
#row + 1,
IF( #prev := `projectid`, 1, 1)
) as rn
FROM roots R
CROSS JOIN (SELECT #row = 0 , #prev = 0) x
ORDER BY `projectid`, `id` DESC -- HERE ID is DESC to get last entry
) T
ON F.`projectid` = T.`projectid`
and F.`rn` = 1
and T.`rn` = 1
OUTPUT
| from | to | projectid |
|------|----|-----------|
| 1 | 2 | 1 |
| 3 | 5 | 2 |
| 3 | 6 | 3 |
| 4 | 2 | 4 |
| 2 | 4 | 5 |
| 6 | 5 | 6 |
NOTE
Change demo query to SELECT * so you can check what is happening. Sometimes last entry equal to first entry.
For your second question depend on if you only count when city are in the first or end.
SELECT *
FROM <previous query>
WHERE (`from` = #CityA and `to`= #CityB )
OR (`from` = #CityB and `to`= #CityA )
Because if you want something considering cityes in between is much more complicated