Take a simple table such as:
User ID | Connection Date
1 | 12/10/2011
2 | 12/12/2011
1 | 12/14/2011
3 | 12/15/2011
1 | 12/16/2011
2 | 12/17/2011
2 | 12/18/2011
1 | 12/19/2011
3 | 12/20/2011
4 | 12/21/2011
2 | 12/21/2011
I would like to calculate the maximum date difference between a users connections.
Thanks
In days ...
select user_id, max(conn_date) - min(conn_date) from conn
group by user_id;
try as below
select
mainqry.userid,
max(mainqry.daysdiff) as max_date_diff
From
(
select
qry.userid,
-- qry.id1,
-- qry.currid,
( Case when qry.id1 = qry.currid then qry.dt1 else null end ) prevconndt,
qry.currdt,
datediff((Case when qry.id1 = qry.currid then qry.dt1 else null end),qry.currdt) daysdiff
from
(
select
userid,
#previd id1,
#previd := userid as currid,
#prevdt dt1,
#prevdt := connectiondate as currdt
from table1,(select #prevdt:=NULL,#previd := NULL) a
order by userid, connectiondate desc
) qry
) MainQry
group by MainQry.userid;
It will output below for your given data
Userid max_date_diff
1 4
2 5
3 5
4 Null
you can also verify it using below ...
select
qry.userid,
-- qry.id1,
-- qry.currid,
( Case when qry.id1 = qry.currid then qry.dt1 else null end ) prevconndt,
qry.currdt,
datediff((Case when qry.id1 = qry.currid then qry.dt1 else null end),qry.currdt) daysdiff
from
(
select
userid,
#previd id1,
#previd := userid as currid,
#prevdt dt1,
#prevdt := connectiondate as currdt
from table1,(select #prevdt:=NULL,#previd := NULL) a
order by userid, connectiondate desc
) qry
Related
This is a sample table:
sample_id | timestamp | p_id
============================================
62054 | 2018-09-25 10:18:15 | 2652
62054 | 2018-09-27 16:44:57 | 966
62046 | null | 1809
62046 | 2018-09-25 10:18:15 | 2097
We need to filter out unique sample_id column, but the logic is
IF the timestamp column is null, then return those null column data
62046 | null | 1809
IF the timestamp column is not null, then return the latest timestamp column data
62054 | 2018-09-27 16:44:57 | 966
So its great if anyone provide the sql query.
We need somethings like that,
WHERE
IF(
NOT NULL = all row group by sample_id,
row where cancelled_at is maximum,
null column
)
This query should give you the results you want. It looks for a row with a NULL timestamp, or a row which has a non-NULL timestamp which is the maximum timestamp for that sample_id, but only if there isn't a row for that sample_id which has a NULL timestamp:
SELECT *
FROM table1 t1
WHERE timestamp IS NULL OR
timestamp = (SELECT MAX(timestamp)
FROM table1 t2
WHERE t2.sample_id = t1.sample_id) AND
NOT EXISTS (SELECT *
FROM table1 t3
WHERE t3.sample_id = t1.sample_id AND
t3.timestamp IS NULL)
Output:
sample_id timestamp p_id
62054 2018-09-27T16:44:57Z 966
62046 (null) 1809
Using variables:
SELECT sample_id, timestamp, p_id
FROM (
SELECT sample_id, timestamp, p_id,
#seq := IF(#s_id = sample_id, #seq + 1,
IF(#s_id := sample_id, 1, 1)) AS seq
FROM mytable
CROSS JOIN (SELECT #s_id := 0, #seq := 0) AS vars
ORDER BY
sample_id,
CASE
WHEN timestamp IS NULL THEN 1
ELSE 2
END,
timestamp DESC
) AS t
WHERE t.seq = 1;
Demo
Explanation:
To understand how this works you need to execute the subquery and examine the output it produces:
SELECT sample_id, timestamp, p_id,
#seq := IF(#s_id = sample_id, #seq + 1,
IF(#s_id := sample_id, 1, 1)) AS seq
FROM mytable
CROSS JOIN (SELECT #s_id := 0, #seq := 0) AS vars
ORDER BY
sample_id,
CASE
WHEN timestamp IS NULL THEN 1
ELSE 2
END,
timestamp DESC
Output:
sample_id timestamp p_id seq
-------------------------------------------
62046 NULL 1809 1
62046 25.09.2018 10:18:15 2097 2
62054 27.09.2018 16:44:57 966 1
62054 25.09.2018 10:18:15 2652 2
You can see here that calculated field seq is used to prioritize records inside each sample_id slice.
Note: If you're on MySQL 8.0 you can use window functions to implement the same logic.
Find out those records where time is not null and filter out
timestamp nulls sample_id and for null timestamp
Use union
select * from t1 where (t1.sample_id,t1.timestamp)
in (
SELECT t.sample_id,max(t.timestamp) AS time
FROM t1 t
WHERE t.sample_id NOT IN (select sample_id from t1 where t1.timestamp is null)
GROUP BY t.sample_id
)
UNION
SELECT *
FROM t1 t
WHERE t.timestamp IS NULL
output
sample_id timestamp p_id
62054 2018-09-27 16:44:57 966
62046 null 1809
Group by on the sample_id.
Using If() function, check if the minimum value of the timestamp for the group is null or not. If it is null, return null, else return the Max() value.
Try the following query:
SELECT sample_id,
IF(MIN(timestamp) IS NULL,
NULL,
MAX(timestamp)) AS timestamp
FROM your_table
GROUP BY sample_id
I am working on a query which is having key value pairs
student
StdId StuName phnNum
1 John 87678
student_meta_data
S.NO field_name field_value StdId
1 college St.Anns 1
2 Address Arizona 1
3 IdNum 321 1
4 Subject Maths 1
5 Marks 90 1
6 Subject Physics 1
7 Marks 80 1
I would like to fetch data from student_meta_data table, for this I had written query like the below,
select
case when student_meta_data.field_name = 'Subject' Then field_value end as subject
case when student_meta_data.field_name ='Marks' Then field_value end as marks
case when student_meta_data.field_name = 'IdNum' Then field_value end as IdNum
from student_meta_data
where student_meta_data.StdId=1
&& student_meta_data.field_name in ('Subject')
for the above query I am fetching records like the below,
subject marks IdNum
null null null
I am expecting to fetch records like below,
subject marks IdNum
Maths 90 321
Physics 80 321
can you one suggest in this. Thanks in advance.
SQL DEMO
SELECT rn,
MAX(subject) as subject,
MAX(case when field_name = 'Marks' Then field_value end) as marks,
MAX(idNum) as idNum
FROM ( SELECT m.*,
#idNum := if(`field_name` = 'IdNum', `field_value`, #idNum) as idNum,
#subject := if(`field_name` = 'Subject', `field_value`, #subject) as subject,
#rn := if (#s = #subject,
#rn,
if(#s := #subject, #rn+1, #rn+1)
) as rn
FROM student_meta_data m
CROSS JOIN (SELECT #idNum := 0, #rn := 0, #subject := '', #s := '' ) as var
ORDER BY `SNO` ) as T
WHERE rn > 0
GROUP BY rn;
OUTPUT
Using variable to track idNum and creating the groups for each subject. First query is just the inner subquery for debug propose, the final is your desire result
Your data model leaves a lot to be desired. Nevertheless, here's something to think about...
SELECT a.field_value subject
, b.field_value marks
FROM
( SELECT x.*
, MIN(y.id) y_id
FROM student_meta_data x
JOIN student_meta_data y
ON y.id > x.id
AND y.field_name = 'marks'
WHERE x.field_name = 'subject'
GROUP
BY x.id
) a
JOIN student_meta_data b
ON b.id = a.y_id;
+---------+-------+
| subject | marks |
+---------+-------+
| Maths | 90 |
| Physics | 80 |
+---------+-------+
I can't stress this enough, but your design needs a change. Normalize it.
Derived from #Juan's answer.
You can do this using user variables and simple filtering on that.
select idnum, subject, marks
from (
select
#idnum := if(field_name='IdNum', field_value, #idnum) as idnum,
#subject := if(field_name='Subject', field_value, #subject) as subject,
#marks := if(field_name='Marks', field_value, #marks) as marks,
m.field_name
from student_meta_data
cross join (
select #idnum := 0,
#subject := null,
#marks := 0
) x
order by sno
) t
where field_name = 'Marks';
I'm filtering based on just Marks field_name because it comes last in the given order and we'll have all the other required values set by then.
Demo
It's possible to create a query that return the x/y number of records?
Eg.
I have table like this
ID | id_user | id_event
23 | 3 | 1
24 | 3 | 1
25 | 3 | 1
26 | 4 | 2
27 | 4 | 2
I will return something that looks like this:
Event
id_user 3 -> **1/3**
id_user 3 -> **2/3**
id_user 3 -> **3/3**
id_user 4 -> **1/2**
id_user 4 -> **2/2**
Any suggestion is appreciated!
Try this
SET #id_event := 0;
SELECT CONCAT('id_user ', id_user ,'->','**', (#id_event := #id_event + 1) ,'/', id_user ,** ) from table
This is probably a duplicate to this question.
SELECT CONCAT('id_user ',id_user,' -> **',rank,'/',group_total,'**') FROM (
SELECT id,
group_total,
CASE id_user
WHEN #id_user THEN
CASE id_event
WHEN #id_event THEN #rowno := #rowno + 1
ELSE #rowno := 1
END
ELSE #rowno :=1
END AS rank,
#id_user := id_user AS id_user,
#id_event := id_event AS id_event
FROM event_table
JOIN (SELECT id_user, id_event, COUNT(*) group_total FROM event_table GROUP BY id_user, id_event) t USING (id_user, id_event)
JOIN (SELECT #rowno := 0, #id_user := 0, #id_event := 0) r
ORDER BY id_user, id_event
) c;
Assuming you want output like this:
id_user < id_user > ** serial number of event related to this user / total events related to this user **
You can accomplish such result by the following query:
SELECT
CONCAT('id_user ',UE.id_user,' -> **',IF(#userID = UE.id_user, #eventNumber := #eventNumber + 1, #eventNumber := 1),'/',t.totalEvents,'**') AS output,
#userID := UE.id_user
FROM (SELECT #userID := -1, #eventNumber := 1) var,user_events UE
INNER JOIN
(
SELECT
id_user,
COUNT(id_event) totalEvents
FROM user_events
GROUP BY id_user
) AS t
ON UE.id_user = t.id_user
ORDER BY UE.id_user;
SQL FIDDLE DEMO
More:
SQL FIDDLE DEMO 2
This particular fiddle returns only the desired output column whereas the first fiddle contains one extra column
I played a little bit and that would be my solution:
SELECT id, id_user, id_event, if(#n = a.id_event, #c:=#c+1, if(#n:=a.id_event, #c:=1, #c:=1)) as count, (SELECT count(*) from TABLE b WHERE a.id_user = b.id_user) as total, from TABLE a join (SELECT #n:= "", #c:=1) c
It just have two if conditions for counting a #c up if #n and id_user matches if not #n become id_user and #c is 1 again. The join is for initialize the var in the same query.
Thx to that question, i found the answer to a questions that i asked 4 days ago.
So I was taking a test recently with some higher level SQL problems. I only have what I would consider "intermediate" experience in SQL and I've been working on this for a day or so now. I just can't figure it out.
Here's the problem:
You have a table with 4 columns as such:
EmployeeID int unique
EmployeeType int
EmployeeSalary int
Created date
Goal: I need to retrieve the difference between the latest two EmployeeSalary for any EmployeeType with more than 1 entry. It has to be done in one statement (nested queries are fine).
Example Data Set: http://sqlfiddle.com/#!9/0dfc7
EmployeeID | EmployeeType | EmployeeSalary | Created
-----------|--------------|----------------|--------------------
1 | 53 | 50 | 2015-11-15 00:00:00
2 | 66 | 20 | 2014-11-11 04:20:23
3 | 66 | 30 | 2015-11-03 08:26:21
4 | 66 | 10 | 2013-11-02 11:32:47
5 | 78 | 70 | 2009-11-08 04:47:47
6 | 78 | 45 | 2006-11-01 04:42:55
So for this data set, the proper return would be:
EmployeeType | EmployeeSalary
-------------|---------------
66 | 10
78 | 25
The 10 comes from subtracting the latest two EmployeeSalary values (30 - 20) for the EmployeeType of 66. The 25 comes from subtracting the latest two EmployeeSalary values (70-45) for EmployeeType of 78. We skip EmployeeID 53 completely because it only has one value.
This one has been destroying my brain. Any clues?
Thanks!
How to make really simple query complex?
One funny way(not best performance) to do it is:
SELECT final.EmployeeType, SUM(salary) AS difference
FROM (
SELECT b.EmployeeType, b.EmployeeSalary AS salary
FROM tab b
JOIN (SELECT EmployeeType, GROUP_CONCAT(EmployeeSalary ORDER BY Created DESC) AS c
FROM tab
GROUP BY EmployeeType
HAVING COUNT(*) > 1) AS sub
ON b.EmployeeType = sub.EmployeeType
AND FIND_IN_SET(b.EmployeeSalary, sub.c) = 1
UNION ALL
SELECT b.EmployeeType, -b.EmployeeSalary AS salary
FROM tab b
JOIN (SELECT EmployeeType, GROUP_CONCAT(EmployeeSalary ORDER BY Created DESC) AS c
FROM tab
GROUP BY EmployeeType
HAVING COUNT(*) > 1) AS sub
ON b.EmployeeType = sub.EmployeeType
AND FIND_IN_SET(b.EmployeeSalary, sub.c) = 2
) AS final
GROUP BY final.EmployeeType;
SqlFiddleDemo
EDIT:
The keypoint is MySQL doesn't support windowed function so you need to use equivalent code:
For example solution in SQL Server:
SELECT EmployeeType, SUM(CASE rn WHEN 1 THEN EmployeeSalary
ELSE -EmployeeSalary END) AS difference
FROM (SELECT *,
ROW_NUMBER() OVER(PARTITION BY EmployeeType ORDER BY Created DESC) AS rn
FROM #tab
) AS sub
WHERE rn IN (1,2)
GROUP BY EmployeeType
HAVING COUNT(EmployeeType) > 1
LiveDemo
And MySQL equivalent:
SELECT EmployeeType, SUM(CASE rn WHEN 1 THEN EmployeeSalary
ELSE -EmployeeSalary END) AS difference
FROM (
SELECT t1.EmployeeType, t1.EmployeeSalary,
count(t2.Created) + 1 as rn
FROM #tab t1
LEFT JOIN #tab t2
ON t1.EmployeeType = t2.EmployeeType
AND t1.Created < t2.Created
GROUP BY t1.EmployeeType, t1.EmployeeSalary
) AS sub
WHERE rn IN (1,2)
GROUP BY EmployeeType
HAVING COUNT(EmployeeType) > 1;
LiveDemo2
The dataset of the fiddle is different from the example above, which is confusing (not to mention a little perverse). Anyway, there's lots of ways to skin this particular cat. Here's one (not the fastest, however):
SELECT a.employeetype, ABS(a.employeesalary-b.employeesalary) diff
FROM
( SELECT x.*
, COUNT(*) rank
FROM employees x
JOIN employees y
ON y.employeetype = x.employeetype
AND y.created >= x.created
GROUP
BY x.employeetype
, x.created
) a
JOIN
( SELECT x.*
, COUNT(*) rank
FROM employees x
JOIN employees y
ON y.employeetype = x.employeetype
AND y.created >= x.created
GROUP
BY x.employeetype
, x.created
) b
ON b.employeetype = a.employeetype
AND b.rank = a.rank+1
WHERE a.rank = 1;
a very similar but faster solution looks like this (although you sometimes need to assign different variables between tables a and b - for reasons I still don't fully understand)...
SELECT a.employeetype
, ABS(a.employeesalary-b.employeesalary) diff
FROM
( SELECT x.*
, CASE WHEN #prev = x.employeetype THEN #i:=#i+1 ELSE #i:=1 END i
, #prev := x.employeetype prev
FROM employees x
, (SELECT #prev := 0, #i:=1) vars
ORDER
BY x.employeetype
, x.created DESC
) a
JOIN
( SELECT x.*
, CASE WHEN #prev = x.employeetype THEN #i:=#i+1 ELSE #i:=1 END i
, #prev := x.employeetype prev
FROM employees x
, (SELECT #prev := 0, #i:=1) vars
ORDER
BY x.employeetype
, x.created DESC
) b
ON b.employeetype = a.employeetype
AND b.i = a.i + 1
WHERE a.i = 1;
I have this Table , (sequence_No.) Field is null :
ID Name age sequence_No.
-- ----- --- ------------
1 sara 20
2 sara 20
3 sara 20
4 john 24
5 john 24
6 Hama 23
I want to Update it to this:
ID Name age sequence_No.
-- ----- --- ------------
1 sara 20 1
2 sara 20 2
3 sara 20 3
4 john 24 1
5 john 24 2
6 Hama 23 1
Which query can do that in mysql?
thank you
You can emulate ROW_NUMBER() using correlated subquery in mysql. The resulting table with sequential number will be join with the table itself and update the value of sequence_No using the generated numbers.
UPDATE tableName a
INNER JOIN
(
SELECT A.ID,
(
SELECT COUNT(*)
FROM tableName c
WHERE c.Name = a.Name AND
c.ID <= a.ID) AS sequence_No
FROM TableName a
) b ON a.ID = b.ID
SET a.sequence_No = b.sequence_No
SQLFiddle Demo
SELECT ID, Name, age, sequence_No
FROM
(
select ID,
Name,
age,
#sum := if(#nme = Name AND #acct = age, #sum ,0) + 1 sequence_No,
#nme := Name,
#acct := age
from TableName,
(select #nme := '', #sum := 0, #acct := '') vars
order by Name, age
) s
ORDER BY ID
Or you may use
SELECT
ID,
Name,
age,
(
CASE Name
WHEN #curType
THEN #curRow := #curRow + 1
ELSE #curRow := 1 AND #curType := Name END
) + 1 AS sequence_No
FROM student, (SELECT #curRow := 0, #curType := '') r
ORDER BY ID,NAME;
this would work though i have not tested yet you can use the same to update your table