I have a table which have records like this
ID DATEADD STATUS
'A0011' '04/01/2018 11:58:31' 'C'
'A0011' '31/05/2019 10:02:36' 'N'
'B0022' '04/01/2018 11:58:31' 'N'
'B0022' '31/05/2019 10:02:36' 'N'
'B0022' '30/04/2020 19:44:36' 'C'
'C0033' '04/01/2018 11:58:31' 'N'
'C0033' '30/05/2019 06:02:36' 'C'
'C0033' '29/04/2020 05:44:36' 'C'
I'm trying to get the Max Date for each ID which have STATUS = 'N'. If I get MAX DATE and STATUS = 'C' then I don't want that record.
Output :
ID DATEADD STATUS
'A0011' '31/05/2019 10:02:36' 'N'
SCRIPT :
SELECT I.* FROM INVOICE I
INNER JOIN (
Select ID,MAX(DATEADD)DATEADD,STATUS FROM INVOICE WHERE STATUS = 'N'
GROUP BY ID,STATUS) O
ON I.ID = O.ID AND O.DATEADD = I.DATEADD
But I'm not able to get desired output.
If your mysql version support the window function, we can try to use ROW_NUMBER window function to get each ID latest DATEADD then compare the STATUS
SELECT *
FROM (
SELECT *,ROW_NUMBER() OVER(PARTITION BY ID ORDER BY DATEADD DESC) rn
FROM INVOICE
) t1
WHERE rn = 1 AND STATUS = 'N'
sqlfiddle
if your MySQL version didn't support the window function we can try to use correlated subquery
SELECT *
FROM (
SELECT *, (SELECT COUNT(*)
FROM INVOICE tt
WHERE tt.ID = t1.ID AND tt.DATEADD > t1.DATEADD) rn
FROM INVOICE t1
) t1
WHERE rn = 1 AND STATUS = 'N'
sqlfiddle
You can use NOT EXISTS:
SELECT i1.*
FROM INVOICE i1
WHERE i1.STATUS = 'N'
AND NOT EXISTS (
SELECT 1
FROM INVOICE i2
WHERE i2.ID = i1.ID
AND STR_TO_DATE(i2.DATEADD, '%d/%m/%Y %H:%i:%s') > STR_TO_DATE(i1.DATEADD, '%d/%m/%Y %H:%i:%s')
);
If the column's DATEADD data type is DATETIME or TIMESTAMP the last condition would be simpler:
...AND i2.DATEADD > i1.DATEADD
See the demo.
We can use ORDER BY and LIMIT 1 to get the row that we want without using any functions, sub-queries, CTE etc.
Thank you to D-Shih for the test schema.
If we want the maximum date with status 'N' for each ID we can use the second query.
SELECT
ID,
DATEADD,
STATUS
FROM INVOICE
ORDER BY
STATUS DESC,
DATEADD DESC
LIMIT 1;
ID | DATEADD | STATUS
:---- | :--------- | :-----
A0011 | 2019-05-31 | N
SELECT
ID,
MAX(DATEADD) AS DATEADD,
STATUS
FROM INVOICE
WHERE STATUS = 'N'
GROUP BY ID
ORDER BY ID;
ID | DATEADD | STATUS
:---- | :--------- | :-----
A0011 | 2019-05-31 | N
B0022 | 2019-05-31 | N
C0033 | 2018-01-04 | N
db<>fiddle here
Related
I am trying to compare value with an alias but it says scolumn not recognized. Docs says can't use alias in where clause
WITH CTE AS
(
SELECT
StockCode AS TopProduct,
COUNT(CustomerID) AS mostCustomers
FROM
dbfinalweek.`e-commerce`
GROUP BY
StockCode
ORDER BY
mostCustomers DESC
LIMIT 1
)
SELECT
StockCode AS stock, CustomerID,
LEAD(StockCode, 1) OVER (ORDER BY CustomerID, InvoiceDate) AS NextItem,
LAG(StockCode, 1) OVER () AS PreviousItem,
InvoiceDate
FROM
dbfinalweek.`e-commerce` AS table1
WHERE
(table1.StockCode = (SELECT CTE.TopProduct FROM CTE)) OR
(table1.NextItem = (SELECT CTE.TopProduct FROM CTE))
Here is my query. Any idea how I could make Table1.NextItem work?
Windo function can't be used directly so you must make a second cte for that`
CREATE TABLE `e-commerce` (StockCode int,CustomerID int,mostCustomers int,InvoiceDate date)
with CTE AS
(
Select
StockCode as TopProduct
,count(CustomerID) as mostCustomers
from `e-commerce`
group by StockCode
order by mostCustomers desc
limit 1
),
cte2 as(
select
StockCode as stock
,CustomerID
, Lead(StockCode,1) over( order by CustomerID,InvoiceDate) as NextItem
,Lag(StockCode,1) over() as PreviousItem
,InvoiceDate
from `e-commerce` )
SELECT
* FROM cte2
WHERE (stock = (Select CTE.TopProduct from CTE)) OR (NextItem=(Select CTE.TopProduct from CTE))
stock | CustomerID | NextItem | PreviousItem | InvoiceDate
----: | ---------: | -------: | -----------: | :----------
db<>fiddle here
I have a following result set:
POST | DATE
--------------------------------------
Senior Software Engg. | 2018-04-18
Software Engg. | 2017-04-18
Assoc. Software Engg. | 2016-04-18
SQL query:
SELECT DISTINCT designation_id as id, d.title as POST, DATE(dt_datetime) as DATE
FROM users_history_check u
INNER JOIN
designations d
ON d.id = u.designation_id
WHERE u.id = $userID
ORDER BY DATE DESC
I want to fetch next record and perform date difference calculation in months, and display records.
Expected Output :
POST | Start DATE | End DATE | MONTHS
---------------------------------------------------------------
Senior Software Engg. | 2018-04-18 | - |
Software Engg. | 2017-04-18 | 2018-04-18 | 12
Assoc. Software Engg. | 2016-04-18 | 2017-04-18 | 12
Something like :
SELECT DISTINCT designation_id as id, d.title as POST, DATE(dt_datetime) as Start DATE, NEXT_RECORD(DATE(dt_datetime)) as End DATE, DATEDIFF(Start DATE, End DATE) as MONTHS....
Any help is very much appreciated. Thanks.
SELECT `POST`,
`DATE`,
IFNULL(END_DATE,'') AS END_DATE,
IFNULL(MONTH,'') AS MONTH
FROM
(SELECT `POST`,
`DATE`,
#prev AS END_DATE,
TIMESTAMPDIFF(month,DATE,#prev) AS MONTH,
#prev := T.DATE AS VarDate
FROM Table1 T,
(SELECT #prev:=null)R
) T1
OUTPUT
POST DATE END_DATE MONTH
Senior Software Engg. 2018-04-18
Software Engg. 2017-04-18 2018-04-18 12
Assoc. Software Engg. 2016-04-18 2017-04-18 12
Demo Link
http://sqlfiddle.com/#!9/33260/15
EXPLANATION:
In Sub query, I am saving the Date value in #prev variable and in each row using that variable to calculate the END_DATE before assigning the current date value from Column Date.
Then using the sub query to present the data in a proper way.
You can get the previous date using variables:
SELECT id, post, date,
(CASE WHEN (#tmp_prevd := #prevd) = NULL THEN NULL -- never happens
WHEN (#prevd := date) = NULL THEN NULL -- never happens
ELSE #tmp_prevd
END) as prev_date
FROM (SELECT DISTINCT designation_id as id, d.title as POST, DATE(dt_datetime) as DATE
FROM users_history_check u INNER JOIN
designations d
ON d.id = u.designation_id
WHERE u.id = $userID
ORDER BY DATE DESC
) ud CROSS JOIN
(SELECT #prevd := NULL) params;
This is tricky, because all references to a variable need to be in the same expression. That is why this uses CASE in a rather arcane way.
In MySQL 8.0 and basically all other databases, you could use LEAD() instead.
Try This....
SELECT T1.POST,T1.DATE ,T2.DATE,DATEDIFF(MONTH,T1.DATE,T2.DATE)
FROM(
SELECT ROW_NUMBER()OVER(ORDER BY DATE DESC) AS SlNo,*
FROM Mytable)T1
LEFT JOIN (SELECT ROW_NUMBER()OVER(ORDER BY DATE DESC)+1 AS SlNo,*
FROM Mytable)T2
ON(T1.SlNo = T2.SlNo )
I suggest using self-join like this
select d1.post,
d1.d `start DATE`,
min(d2.d) `end DATE`,
timestampdiff(month, d1.d, min(d2.d)) `MONTHS`
from data d1
left join data d2 on d1.d < d2.d
group by d1.post, d1.d
dbfiddle demo
The data table is the result of your SQL. It can be added using WITH or you may use subquery as well.
SELECT * ,Datediff(Month,[Date],endate)
FROM
(
SELECT *,Lead( [Date], 1, Null) OVER (
ORDER BY [Date]) AS Endate --INTO SourceTable
FROM
(
SELECT 'Senior Software Engg.' POST , '2018-04-18' DATE UNION ALL
SELECT 'Software Engg.' POST , '2017-04-18' DATE UNION ALL
SELECT 'Assoc. Software Engg.' POST , '2016-04-18' DATE
)A
)B
ORDER BY [Date] desc
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 a MySQL table of the following form
account_id | call_date
1 2013-06-07
1 2013-06-09
1 2013-06-21
2 2012-05-01
2 2012-05-02
2 2012-05-06
I want to write a MySQL query that will get the maximum difference (in days) between successive dates in call_date for each account_id. So for the above example, the result of this query would be
account_id | max_diff
1 12
2 4
I'm not sure how to do this. Is this even possible to do in a MySQL query?
I can do datediff(max(call_date),min(call_date)) but this would ignore dates in between the first and last call dates. I need some way of getting the datediff() between each successive call_date for each account_id, then finding the maximum of those.
I'm sure fp's answer will be faster, but just for fun...
SELECT account_id
, MAX(diff) max_diff
FROM
( SELECT x.account_id
, DATEDIFF(MIN(y.call_date),x.call_date) diff
FROM my_table x
JOIN my_table y
ON y.account_id = x.account_id
AND y.call_date > x.call_date
GROUP
BY x.account_id
, x.call_date
) z
GROUP
BY account_id;
CREATE TABLE t
(`account_id` int, `call_date` date)
;
INSERT INTO t
(`account_id`, `call_date`)
VALUES
(1, '2013-06-07'),
(1, '2013-06-09'),
(1, '2013-06-21'),
(2, '2012-05-01'),
(2, '2012-05-02'),
(2, '2012-05-06')
;
select account_id, max(diff) from (
select
account_id,
timestampdiff(day, coalesce(#prev, call_date), call_date) diff,
#prev := call_date
from
t
, (select #prev:=null) v
order by account_id, call_date
) sq
group by account_id
| ACCOUNT_ID | MAX(DIFF) |
|------------|-----------|
| 1 | 12 |
| 2 | 4 |
see it working live in an sqlfiddle
If you have an index on account_id, call_date, then you can do this rather efficiently without variables:
select account_id, max(call_date - prev_call_date) as diff
from (select t.*,
(select t2.call_date
from table t2
where t2.account_id = t.account_id and t2.call_date < t.call_date
order by t2.call_date desc
limit 1
) as prev_call_date
from table t
) t
group by account_id;
Just for educational purposes, doing it with JOIN:
SELECT t1.account_id,
MAX(DATEDIFF(t2.call_date, t1.call_date)) AS max_diff
FROM t t1
LEFT JOIN t t2
ON t2.account_id = t1.account_id
AND t2.call_date > t1.call_date
LEFT JOIN t t3
ON t3.account_id = t1.account_id
AND t3.call_date > t1.call_date
AND t3.call_date < t2.call_date
WHERE t3.account_id IS NULL
GROUP BY t1.account_id
Since you didn't specify, this shows max_diff of NULL for accounts with only 1 call.
SELECT a1.account_id , max(a1.call_date - a2.call_date)
FROM account a2, account a1
WHERE a1.account_id = a2.account_id
AND a1.call_date > a2.call_date
AND NOT EXISTS
(SELECT 1 FROM account a3 WHERE a1.call_date > a3.call_date AND a2.call_date < a3.call_date)
GROUP BY a1.account_id
Which gives :
ACCOUNT_ID MAX(A1.CALL_DATE - A2.CALL_DATE)
1 12
2 4
I have a query which returns the number of rows of a distinct device_type which occur more than once.
SELECT COUNT(*) AS C1,device_type FROM stat
WHERE stat_date = '2012-02-08'
GROUP BY 2 HAVING C1 > 1
ORDER BY 1 DESC
I would like to summarize the remaining (HAVING count = 1) rows as 'others'
How can I add the sum of COUNT(*) and 'others' as second column for the following query?
SELECT COUNT(*) AS C2,device_type FROM stat
WHERE stat_date = '2012-02-08'
GROUP BY 2 HAVING C2 = 1
ORDER BY 1 DESC
Sample data in DB
device_type
dt1
dt1
dt1
dt2
dt2
dt3
dt4
dt5
expected result
3 dt1
2 dt2
3 other
I would do this.
SELECT COUNT(*) AS C1,device_type FROM stat
WHERE stat_date = '2012-02-08'
GROUP BY 2 HAVING C1 > 1
ORDER BY 1 DESC
Union
SELECT Sum(1),'OTHERS'FROM stat
WHERE stat_date = '2012-02-08'
GROUP BY 2 HAVING C1 =1
ORDER BY 1 DESC
You can also try:
SELECT SUM(C1) AS C1, CASE WHEN C1 = 1 THEN 'other' ELSE device_type END as device_type
FROM ( SELECT COUNT(*) AS C1,
device_type
FROM stat
WHERE stat_date = '2012-02-08'
GROUP BY device_type) A
GROUP BY CASE WHEN C1 = 1 THEN 'other' ELSE device_type END