I have a table with the following columns, where the timestamp column indicates the date the user viewed a product, and the purchase column if the view generated a purchase:
id
userId
productId
timestamp
purchase
1
2
4
2022-01-07
0
2
2
4
2022-01-10
1
3
2
4
2022-01-12
0
4
2
4
2022-01-16
1
How to group the data by userId and productId in order to return as follows:
id
userId
productId
firstView
lastView
firstPurchase
1
2
4
2022-01-07
2022-01-16
2022-01-10
For the firstView and listView columns I know I should use Min() and Max(), but the firstPurchase column I can't display. I tried using CASE when column purchase = 1 but it didn't work.
You need to use MIN(CASE WHEN purchase = 1 THEN "timestamp" ELSE NULL END), like this:
create table your_table (id int, userId int, productId int, "timestamp" date, purchase int)
insert into your_table (id, userId, productId, "timestamp", purchase)
values
(1,2,4,'2022-01-07 00:00:00',0),
(2,2,4,'2022-01-10 00:00:00',1),
(3,2,4,'2022-01-12 00:00:00',0),
(4,2,4,'2022-01-16 00:00:00',1);
SELECT
userId,
productId,
MIN(timestamp) AS firstView,
MAX(timestamp) AS lastView,
MIN(CASE WHEN purchase = 1 THEN "timestamp" ELSE NULL END) AS firstPurchase
FROM your_table
GROUP BY
userId,
productId;
p.s. please don't call a column "timestamp" :)
Try this:
SELECT t1.id, t1.userId, t1.productId, t1.timestamp,
(SELECT t3.timestamp
FROM yourtable t3
WHERE t3.userId = t1.userId
AND t3.productId = t1.productId
AND NOT EXISTS
(SELECT 1
FROM yourtable t4
WHERE t4.userId = t3.userId
AND t4.productId = t3.productId
AND t4.timestamp > t3.timestamp
)
) as lastView,
(SELECT t3.timestamp
FROM yourtable t3
WHERE t3.userId = t1.userId
AND t3.productId = t1.productId
AND t3.purchase = 1
AND NOT EXISTS
(SELECT 1
FROM yourtable t4
WHERE t4.userId = t3.userId
AND t4.productId = t3.productId
AND t4.timestamp < t3.timestamp
AND t4.purchase = 1
)
) as firstPurchase
FROM yourtable t1
WHERE NOT EXISTS
(SELECT 1 FROM yourtable t2
WHERE t1.userId = t2.userId
AND t1.productId = t2.productId
AND t2.timestamp < t1.timestamp)
See Sql Fiddle
Related
I like to add event duration to a previous record every time a new record gets added.
This is what I have
ID EventType EventTime EventDuration
-------------------------------------
1 TypeA 10:20 NULL
2 TypeB 09:30 NULL
3 TypeC 08:00 NULL
This is what I want to achieve:
ID EventType EventTime EventDuration
-------------------------------------
1 TypeA 10:20 00:50
2 TypeB 09:30 01:30
3 TypeC 08:00 ...
4 ... ...
When a new records gets added (with ID, EventType and EventTime), the duration of the previous record (timediff between TypeB and Type A) should be added to the previous record in column EventDuration.
What I have so far is:
SELECT
id, EventTime,
timestampdiff(minute,
(SELECT EventTime
FROM TableName t2
WHERE t2.id < t1.id ORDER BY t2.id DESC LIMIT 1),EventTime)
AS EventDuration
FROM records t1
WHERE id = ....<this is where I get stuck, this should be a query that identifies the ID of latest EventTime record>
Any suggestions?
(I am running MySQL 5.6.39)
If you are running MySQL 8.0, you can use window functions for this:
update mytable t
inner join (
select id, timediff(eventTime, lag(eventTime) over(order by eventTime)) event_duration
from mytable t
) t1 on t1.id = t.id
set t.event_duration = t1.event_duration
If you want to update only the last but 1 record, you can order by and limit in the subquery (or in the outer query):
update mytable t
inner join (
select id, timediff(eventTime, lag(eventTime) over(order by eventTime)) event_duration
from mytable t
order by id desc
limit 1, 1
) t1 on t1.id = t.id
set t.event_duration = t1.event_duration
In earlier versions, one option is to emulate lag() with a window function:
update mytable t
inner join (
select
id,
timediff(
(select min(eventTime) from mytable t1 where t1.eventTime > t.eventTime),
eventTime
) event_duration
from mytable t
) t1 on t1.id = t.id
set t.event_duration = t1.event_duration
I'm trying to do a request with a group BY.
Here is an exemple of my table ticket :
id DtSell Price Qt
1 01-01-2017 3.00 1
1 02-01-2017 2.00 3
2 01-01-2017 5.00 5
2 02-01-2017 8.00 2
And my request :
SELECT id, Price, sum(Qt) FROM ticket
GROUP BY id;
but unfortunately, the price returned is not necessarily the right one; I would like to have the last price according to DtSell like that :
id Price sum(Qt)
1 2.00 4
2 8.00 7
But i didn't find how to do it.
Can you help me ?
Thank you in advance!!
You might need a sub query,try below:
SELECT
t1.id,
(SELECT t2.price FROM ticket t2 WHERE t2.id=t1.id
ORDER BY t2.DtSell DESC LIMIT 1 ) AS price,
SUM(t1.Qt)
FROM ticket t1 GROUP BY t1.id;
You can do this with a group_concat()/substring_index() trick:
SELECT id, Price, SUM(Qt)
SUBSTRING_INDEX(GROUP_CONCAT(price ORDER BY dtsell DESC), ',' 1) as last_price
FROM ticket
GROUP BY id;
Two notes:
This is subject to internal limits on the length of the intermediate string used for GROUP_CONAT() (a limit that can easily be changed).
It changes the type of price to a string.
Try this query.
SELECT id, Price, sum(Qt) FROM ticket
GROUP BY id,Price
Your Output;
id Price sum(Qt)
1 3.00 4
2 8.00 7
You can select all rows from ticket grouped by id ( to sum quantity), then join to the rows which have the max dtsell for each id group( to select the price).
http://sqlfiddle.com/#!9/574cb9/8
SELECT t.id
, t3.price
, SUM(t.Qt)
FROM ticket t
JOIN ( SELECT t1.id
, t1.price
FROM ticket t1
JOIN ( SELECT id
, MAX(dtsell) dtsell
FROM ticket
GROUP BY id ) t2
ON t1.id = t2.id
AND t1.dtsell = t2.dtsell ) t3
ON t3.id = t.id
GROUP BY t.id;
You can do it like this:
declare #t table (id int, dtsell date, price numeric(18,2),qt int)
insert into #t
values
(1 ,'01-01-2017', 3.00 , 1),
(1 ,'02-01-2017', 2.00 , 3),
(2 ,'01-01-2017', 5.00 , 5),
(2 ,'02-01-2017', 8.00 , 2)
select x.id,price,z.Qt from (
select id,price,dtsell,row_number() over(partition by id order by dtsell desc ) as rn from #t
)x
inner join (select SUM(qt) as Qt,ID from #t group by id ) z on x.id = z.id
where rn = 1
Can someone help in simplifying below query. Cost of it shows as 58.
b.dueDate and b.tID are composite key.
a.tID is primary key and foreign key between table 1 and 2.
SELECT test.tID, test.sor_acct_id, test.pmt, test.status FROM ct.tab1 a,
(SELECT a.tID, a.sor_acct_id, b.dueDate, b.amt, b.status, a.pmt,
Row_number() OVER ( partition BY a.tID ORDER BY b.dueDate DESC) AS rn
FROM ct.tab1 a
INNER JOIN ct.tab2 b
ON a.tID = b.tID
WHERE a.tID IN (SELECT a.tID
FROM ct.tab1 a
INNER JOIN
ct.tab2 b
ON a.tID =
b.tID
WHERE a.status = 'E'
AND a.pmt IS NOT NULL
AND a.pmt <> '{}'
AND b.dueDate > CURRENT_DATE - 4
AND b.dueDate < CURRENT_DATE)
AND b.dueDate > CURRENT_DATE - 1
) test WHERE rn = 1
AND test.status IN ( 'X', 'Z' )
AND a.tID = test.tID
Maybe you would change:
WHERE a.tID IN (SELECT a.tID ....
into:
join ((SELECT a.tID FROM ct.tab1 a ....) t on t.tID=a.tID
tID is tab1's primary key. So when you say you are looking for tab1 records the tID of which is found in a set of tab1 records with status E, you could just as well simply say: I'm looking for tab1 records with status E.
What your query does is: Show all tab1 records with their final tab2 status provided ...
the tab1 pmt is not null and not '{}'
the tab1 status is E
the final tab2 status is X or Z
the final tab2 status is due today or in the future
there exists at least one tab2 record due in the last three days for the tab1 record
The query:
SELECT
t1.tID,
t1.sor_acct_id,
t1.pmt,
t2.status
FROM ct.tab1 t1
join
(
SELECT
tID,
status,
ROW_NUMBER() OVER (PARTITION BY tID ORDER BY dueDate DESC) AS rn
FROM ct.tab2
WHERE dueDate > CURRENT_DATE - 1
) t2 ON t2.tID = tab1.tID AND t2.rn = 1 AND t2.status IN ('X', 'Z')
WHERE t1.status = 'E'
AND t1.pmt IS NOT NULL
AND t1.pmt <> '{}'
and t1.tID IN
(
SELECT tID
FROM ct.tab2
WHERE dueDate > CURRENT_DATE - 4
AND dueDate < CURRENT_DATE
);
table : transmission
--------------------------------------------------------
ID ReqString Timestamp Actif
------- ------------- --------------------- --------
a O21 2016-05-02 10:03:27 1
a O20 2016-05-01 11:07:47 1
a O11 2016-05-02 09:27:53 1
b O20 2016-05-02 12:27:45 1
b O21 2016-05-01 09:32:55 1
I need to retrieve, for the same id, the latest values for ReqString LIKE O2% AND LIKE O1%
I have tried this LEFT JOIN. This query works when I have a value in t1, but not working when I have no value for the table t1...
SELECT t1.ReqString AS O1, t2.ReqString AS O2, t1.Timestamp AS T1, t2.Timestamp AS T2
FROM transmission t1
LEFT JOIN transmission t2 ON t2.ID = t1.ID
AND t2.ReqString LIKE 'O2%'
AND t2.Actif=1
WHERE t1.ID = 'b'
AND t1.ReqString LIKE 'O1%'
AND t1.Actif = 1
ORDER BY t1.Timestamp DESC, t2.Timestamp DESC
LIMIT 1
So if I run the query for the ID = 'a', I need to get
------------------------------------------------------------------------
O1 O2 T1 T2
------- --------- ----------------------- -------------------------
O11 O21 2016-05-02 09:27:53 2016-05-02 10:03:27
and if I run it for the ID = 'b', the result I would like to have is
------------------------------------------------------------------------
O1 O2 T1 T2
------- --------- ----------------------- -------------------------
NULL O20 NULL 2016-05-02 12:27:45
select t1.ReqString AS O1, t2.ReqString AS O2, t1.Timestamp AS T1, t2.Timestamp AS T2 from
(SELECT ReqString , Timestamp
FROM transmission where ReqString LIKE 'O1%' AND Actif=1 and ID = 'a'
limit 1 order by Timestamp DESC)t1,
(SELECT ReqString , Timestamp
FROM transmission where ReqString LIKE 'O2%' AND Actif=1 and ID = 'a'
limit 1 order by Timestamp DESC)t2
Try this:
SELECT
result1.ReqString as 'O1',
result2.ReqString as 'O2',
result1.Timestamp as 'T1',
result2.TimeStamp as 'T2'
FROM
(
SELECT
#i:=#i+1 AS rowId,
ReqString,
Timestamp
FROM transmission,(SELECT #i:=0) a
WHERE ReqString LIKE 'O1%'
AND Actif=1
AND ID = 'a'
LIMIT 1
ORDER BY Timestamp DESC
) as result1
LEFT JOIN
(
SELECT
#j:=#j+1 AS rowId,
ReqString,
Timestamp
FROM transmission,(SELECT #j:=0) a
WHERE ReqString LIKE 'O2%'
AND Actif=1
AND ID = 'a'
LIMIT 1
ORDER BY Timestamp DESC
) as result2
ON result1.rowId = result2.rowId;
I have a feeling that left join may not be what you are looking for. This should produce the desired result whether or not there is a value in result1. If it does not provide the result needed, let me know what is wrong.
I have this table e.g.:
Id StatusDate Status
1 20-08-2014
1 15-08-2014
1 09-08-2014 P
2 17-08-2014
1 10-08-2014
2 12-08-2014
2 06-07-2014 P
1 30-07-2014
2 02-07-2014
2 01-07-2014 P
...... and so on
I want to select count by ID where status is blank until I hit the first 'P' in ascending order of date group by ID. So my results will be like this.
ID Count
1 3
2 2
Try it out. Not tested
SELECT t1.ID, count(*) FROM table t1
WHERE t1.StatusDate >= (SELECT MAX(t2.StatusDate) FROM table t2
WHERE t1.ID = t2.ID AND t2.Status = 'P')
GROUP BY t1.ID
Assuming your table name is StatusTable This will work:
SELECT
ID,
COUNT(*) AS `Count`
FROM StatusTable AS st
WHERE
st.Status = ''
AND st.StatusDate > (
SELECT st2.StatusDate
FROM `StatusTable` AS st2
WHERE st.ID = st2.ID
AND st2.Status = 'P'
ORDER BY st2.StatusDate DESC
LIMIT 1
)
GROUP BY st.ID
ORDER BY st.ID
One option is to use a JOIN and COUNT rows which have a lower statusdate value, like this:
SELECT t1.id, SUM(CASE WHEN t1.statusdate > t2.statusdate THEN 1 ELSE 0 END) AS mycount
FROM t t1 JOIN (
SELECT id, MIN(statusdate) statusdate
FROM t
WHERE status = 'P'
GROUP BY id
) t2
ON t1.id = t2.id
GROUP BY t1.id
Working Demo: http://sqlfiddle.com/#!2/d9d91/2