sql update max(date) group by - mysql

raw data
no
group
date
value
flag
1
a
2022-10-13
old
y
2
a
2022-10-15
new
y
3
b
2022-01-01
old
n
4
b
2022-01-03
new
n
step1. insert no1 raw
step2. modify date value using by no2 raw
and I want to update latest date no1 raw using by no2 raw
and the condition is where `flag` = "y"
final sql table
no
group
date
value
flag
1
a
2022-10-15
old
y
3
b
2022-01-01
old
n
is it possible?
+) I insert/update raw data line by line.

Not entirely clear but I hope below answer gives you a hint if not the solution.
select no,
`group`,
case when flag='Y' then mx_dt else `date` end as new_date,
value,
flag
from ( select no,
`group`,
value,
`date`,
flag ,
row_number() over(partition by `group` order by `date` asc ) as rn,
max(`date`) over (partition by `group`,(case when flag <> 'Y' then `date` end) ) mx_dt
from raw_data
) as tbl
where rn=1;
Above code will select the max(date) per group if the flag=Y otherwise it will take the date per row.
https://dbfiddle.uk/JhRUti2h

The solution is to self join the source table and select the right field, prioritizing the latest date.
Here you have a working query:
WITH source_data AS (
SELECT 1 AS no_, 'a' AS group_, CAST('2022-10-13' AS DATE) AS date, 'old' AS value, 'y' AS flag
UNION ALL
SELECT 2, 'a', CAST('2022-10-15' AS DATE), 'new', 'y'
UNION ALL
SELECT 3, 'b', CAST('2022-01-01' AS DATE), 'old', 'n'
UNION ALL
SELECT 4, 'b', CAST('2022-01-03' AS DATE), 'new', 'n')
SELECT no_, group_, COALESCE(new_date, date), value, flag
FROM
(SELECT * FROM source_data WHERE value = 'old') old_values
LEFT JOIN (SELECT group_ AS new_group, date AS new_date FROM source_data WHERE value = 'new' AND flag='y') new_values
ON old_values.group_ = new_values.new_group
The result is what you expected:
no_ group_ f0_ value flag
1 a 2022-10-15 old y
3 b 2022-01-01 old n

Related

select sum until certain amount and then update certain fields based on condition

I have a query that changes the status of some fields and rate of another field.
fiddle expample
CREATE TABLE mytable(
item_id INTEGER NOT NULL PRIMARY KEY
,rate INTEGER NOT NULL
,status VARCHAR(6) NOT NULL
);
INSERT INTO mytable(item_id,rate,status) VALUES (1,12,'credit');
INSERT INTO mytable(item_id,rate,status) VALUES (2,10,'credit');
INSERT INTO mytable(item_id,rate,status) VALUES (3,10,'credit');
INSERT INTO mytable(item_id,rate,status) VALUES (4,20,'cash');
INSERT INTO mytable(item_id,rate,status) VALUES (5,55,'credit');
select item_id, 'cash' as status,
case when sum_rate >= 23 then sum_rate - 23 else rate end as rate
from (
select t.*, sum(rate) over(order by item_id) sum_rate
from mytable t
where status = 'credit'
) t
where sum_rate - rate < 23;
update mytable t
inner join (
select item_id, sum(rate) over(order by item_id) sum_rate
from mytable t
where status = 'credit'
) t1 on t1.item_id = t.item_id
set
t.status = 'cash',
t.rate = case when t1.sum_rate >= 23 then t1.sum_rate - 23 else t.rate end
where t1.sum_rate - t.rate < 23
The logic checks the sum of all rows until a value of 23 is reached and changes the status of those rows to cash, in the example since the total of rate in top 3 rows is greater than 23, the third row is updated with the balance after adding the first two rows. I want the third row status to remain the same and only the rate to be updated.
The problem the above code is that it updates all the status of all rows having sum of rate 23.
The original question for reference
Ok I was able to get it done with the following addition to the query so posting it in case it may help anyone with a similar requirement.
t.status = case when t1.sum_rate >= 23 then 'credit' else 'cash' end

Get Data According to Group by date field

Here is my table
Which have field type which means 1 is for income and 2 is for expense
Now requirement is for example in table there is two transaction made on 2-10-2018 so i want data as following
Expected Output
id created_date total_amount
1 1-10-18 10
2 2-10-18 20(It calculates all only income transaction made on 2nd date)
3 3-10-18 10
and so on...
it will return an new field which contains only incom transaction made on perticulur day
What i had try is
SELECT * FROM `transaction`WHERE type = 1 ORDER BY created_date ASC
UNION
SELECT()
//But it wont work
SELECT created_date,amount,status FROM
(
SELECT COUNT(amount) AS totalTrans FROM transaction WHERE created_date = created_date
) x
transaction
You can Also See Schema HERE http://sqlfiddle.com/#!9/6983b9
You can Count() the total number of expense transactions using conditional function If(), on a group of created_date.
Similarly, you can Sum() the amount of expense done using If(), on a created_date.
Try the following:
SELECT
`created_date`,
SUM(IF (`type` = 2, `amount`, 0)) AS total_expense_amount,
COUNT(IF (`type` = 2, `id`, NULL)) AS expense_count
FROM
`transaction`
GROUP BY `created_date`
ORDER BY `created_date` ASC
Do you just want a WHERE clause?
SELECT t.created_date, SUM(amount) as total_amount
FROM transaction t
WHERE type = 2
GROUP BY t.created_date
ORDER BY created_date ASC ;

MYSQL - Filter consecutive not null dates

Get only the biggest date:
These are check-in and check-out records of employees, some times they do twice or more entries on the system in a row. In this sample there were two check-out in a row. Assuming these rows always gonna be ordered, in the case of check-out I would like have the biggest date, and in the case of the check-in the smallest date.
In that case I would like to have this:
The smaller date was excluded:
DEMO
Try this, in this big CASE statement I increment column by one, if checkin switches from null to not null and the other way around. Then it's enough to group by this column taking max and min of checkout and checkin respectively:
select #checkinLag := null, #rn := 0;
select max(id),
functionario,
loja,
min(checkin),
max(checkout)
from (
select case when (checkinLag is null and checkin is not null) or
(checkinLag is not null and checkin is null)
then #rn := #rn + 1 else #rn end rn,
checkin,
checkout,
loja,
id,
functionario
from (
select #checkinLag checkinLag,
#checkinLag := checkin,
checkin,
checkout,
loja,
id,
functionario
from dummyTable
order by coalesce(checkin, checkout)
) a
) a group by functionario, loja, rn
I have used subqueries, to guarantee order of evaluating expressions (assigning and using of #checkinLag), as Gordon Linoff pointed.
Demo
My solution:
Select
*
from dummyTable base
where (base.checkout is null or not exists (
select
1
from dummyTable co
where co.checkout between base.checkout and DATE_ADD(base.checkout, INTERVAL 5 SECOND)
and base.id <> co.id
and base.functionario = co.functionario
and base.loja = co.loja
)) and (base.checkin is null or not exists (
select
1
from dummyTable ci
where ci.checkin between DATE_SUB(base.checkin, INTERVAL 5 SECOND) and base.checkin
and base.id <> ci.id
and base.functionario = ci.functionario
and base.loja = ci.loja
));
you can test the query here. There is no need that the rows are orderd. I choose 5 seconds as the interval where check-in/outs should be ignored.

How to select last and last but one records

I have a table with 3 columns id, type, value like in image below.
What I'm trying to do is to make a query to get the data in this format:
type previous current
month-1 666 999
month-2 200 15
month-3 0 12
I made this query but it gets just the last value
select *
from statistics
where id in (select max(id) from statistics group by type)
order
by type
EDIT: Live example http://sqlfiddle.com/#!9/af81da/1
Thanks!
I would write this as:
select s.*,
(select s2.value
from statistics s2
where s2.type = s.type
order by id desc
limit 1, 1
) value_prev
from statistics s
where id in (select max(id) from statistics s group by type) order by type;
This should be relatively efficient with an index on statistics(type, id).
select
type,
ifnull(max(case when seq = 2 then value end),0 ) previous,
max( case when seq = 1 then value end ) current
from
(
select *, (select count(*)
from statistics s
where s.type = statistics.type
and s.id >= statistics.id) seq
from statistics ) t
where seq <= 2
group by type

How to select rows as a column for View in TSQL?

Assume I have 3 tables: Animal, CareTaker, and Apppointment. Schema, with some data like so:
Create Table Animal (Id int identity, Name varchar(25))
Create Table CareTaker(Id int identity, Name varchar(50))
Create Table Appointments(Id int identity, AnimalId int, CareTakerId int, AppointmentDate DateTime, BookingDate DateTime)
Insert into Animal(Name) Values('Ghost'), ('Nymeria'), ('Greywind'), ('Summer')
Insert into CareTaker(Name) Values ('Jon'), ('Arya'), ('Rob'), ('Bran')
Insert into Appointments(AnimalId, CareTakerId, AppointmentDate, BookingDate) Values
(1, 1, GETDATE() + 7, GetDate()), -- Ghost cared by Jon
(1, 2, GETDATE() + 6, GetDate()), -- Ghost cared by Arya
(4, 3, GETDATE() + 8, GetDate()) -- Summer cared by Rob
I want to select only 3 caretakers for each animal as a columns. Something like this:
I don't care about other appointments, just the next three, for each animal. If there aren't three appointments, it can be blank / null.
I'm quite confused about how to do this.
I tried it with Sub queries, something like so:
select Name,
-- Care Taker 1
(Select Top 1 C.Name
From Appointments A
Join CareTaker C on C.Id = A.CareTakerId
Where A.AppointmentDate > GETDATE()
And A.AnimalId = Animal.Id
Order By AppointmentDate) As CareTaker1,
-- Appointment Date 1
(Select Top 1 AppointmentDate
From Appointments
Where AppointmentDate > GETDATE()
And AnimalId = Animal.Id
Order By AppointmentDate) As AppointmentDate1
From Animal
But for the second caretaker, I would have to go second level select on where clause to exclude the id from top 1 (because not sure how else to get second row), something like select top 1 after excluding first row id; where first row id is (select top 1) situtation.
Anyhow, that doesn't look like a great way to do this.
How can I get the desired output please?
You can get all the information in rows using:
select an.name as animal, ct.name as caretaker, a.appointmentdate
from appointments a join
animals an
on a.id = an.animalid join
caretaker c
on a.caretakerid = c.id;
Then, you basically want to pivot this. One method uses the pivot keyword. Another conditional aggregation. I prefer the latter. For either, you need a pivot column, which is provided using row_number():
select animal,
max(case when seqnum = 1 then caretaker end) as caretaker1,
max(case when seqnum = 1 then appointmentdate end) as appointmentdate1,
max(case when seqnum = 2 then caretaker end) as caretaker2,
max(case when seqnum = 2 then appointmentdate end) as appointmentdate2,
max(case when seqnum = 3 then caretaker end) as caretaker3,
max(case when seqnum = 3 then appointmentdate end) as appointmentdate3
from (select an.name as animal, ct.name as caretaker, a.appointmentdate,
row_number() over (partition by an.id order by a.appointmentdate) as seqnum
from appointments a join
animals an
on a.id = an.animalid join
caretaker c
on a.caretakerid = c.id
) a
group by animal;