please advice how to make SQL query in order to get from this table
ID|Number|Type|
----------------
1 |AA1 |IN |
2 |AA2 |OUT |
3 |AA3 |IN |
4 |AA4 |OUT |
into this result
ID| IN | OUT |
-------------------
1 | AA1 | AA2 |
2 | AA3 | AA4 |
Thanks
This Will work using Implicit join.
It will use mysql session variables. for reference, you can read http://www.mysqltutorial.org/mysql-variables/ for session variables.
SET #row_number = 0;
SET #row_number2 = 0;
SELECT
out_table.OUTs AS outs, in_table.Ins as INs FROM
(SELECT
(#row_number2:=#row_number2 + 1) AS num2, Number as OUTs FROM your_table WHERE your_table.Type = 'OUT') as out_table ,
(SELECT
(#row_number:=#row_number + 1) AS num1, Number as Ins FROM your_table WHERE your_table.Type = 'IN') as in_table
WHERE num2 = num1
You can emulate row_number like functionality, using session variables. We get all INs and OUTs separately in two derived tables and do a LEFT JOIN on them, to get the desired output.
This will work even for the cases where IN and OUT are not consecutive. It will also handle the cases where there is an IN without OUT.
It would not work for the case when there is an OUT without IN.
Try the following query:
SET #row_no_1 = 0;
SET #row_no_2 = 0;
SELECT
t1.row_no AS ID, t1.Number AS `IN`, t2.Number AS `OUT`
FROM
(SELECT
#row_no_1:=#row_no_1 + 1 AS row_no, Number
FROM
`your_table`
WHERE
Type = 'IN'
ORDER BY id ASC) AS t1
LEFT JOIN
(SELECT
#row_no_2:=#row_no_2 + 1 AS row_no, Number
FROM
`your_table`
WHERE
Type = 'OUT'
ORDER BY id ASC) AS t2 ON t2.row_no = t1.row_no
answering myself...
SELECT a.ID
MAX(CASE WHEN a.type = "IN" THEN a.Number ELSE "" END) AS IN_Type,
MAX(CASE WHEN b.type = "IN" THEN b.Number ELSE "" END) AS Out_Type
FROM table1 a Left join table1 b on a.ID = b.ID
Group by a.ID
Related
having a table structure of id and a name:
create table Mytable (
id integer not null,
name varchar(30) not null,
unique(id)
);
insert into Mytable (id,name) values
(1 , 'one'),
(2 , 'two'),
(3 , 'three'),
(4 , 'four'),
(6 , 'six');
How may I get a mix of even and odd rows in a result table like:
even | odd
-----------
null one '0 is not in Mytable so it puts null value
two three
four null '5 and 6 are not in Mytable so it puts null value
six null
I was trying to first get the following as a template and use it later as
a dictionary:
SELECT MIN(id-1) as id,MAX(id-1) as col
FROM Mytable
GROUP BY FLOOR((id+1)/2);
I get:
id col
0 1
2 3
5 5
But I do not know how to continue
For MySQL Version <= 5.7, You can use the below query
Query 1:
SELECT
MAX(CASE WHEN m.id % 2 = 0 THEN name END) AS even,
MAX(CASE WHEN m.id % 2 = 1 THEN name END) AS odd
FROM
(
SELECT
(SELECT MAX(id) FROM mytable) AS maxid,
#rn := #rn + 1 AS rn,
(SELECT IF((#rn * 2) <= maxid, #rn, NULL)) AS rid
FROM
mytable
JOIN
(SELECT #rn := -1) AS var
) AS t
JOIN
mytable m ON FLOOR(m.id/2) = t.rid
GROUP BY rid;
Result 1:
even | odd
:--- | :----
null | one
two | three
four | null
six | null
Demo 1:
db fiddle
Query 2:
After confirmation based on #Madhur Bhaiya comment. If there is no row for id = 8 and 9 then it will show null, null.
SELECT
MAX(CASE WHEN m.id % 2 = 0 THEN name END) AS even,
MAX(CASE WHEN m.id % 2 = 1 THEN name END) AS odd
FROM
(
SELECT
(SELECT MAX(id) FROM mytable) AS maxid,
#rn := #rn + 1 AS rn,
(SELECT IF((#rn * 2) <= maxid, #rn, NULL)) AS rid
FROM
(SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t
JOIN
(SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t2
JOIN
(SELECT #rn := -1) var -- currently it will return 1..100, if needed more add joins based on your needs
) AS t
LEFT JOIN
mytable m ON FLOOR(m.id/2) = t.rid
GROUP BY rid HAVING rid IS NOT NULL;
Result 2:
even | odd
:------ | :-----
null | one
two | three
four | null
six | null
null | null
null | eleven
null | null
null | null
sixteen | null
Demo 2:
db fiddle
For MySQL Version > 8.0, You can use #Nick query but if you need null, null like Result 2 mentioned for <= v5.7 then add LEFT JOIN with ORDER BY clause.
Query:
with recursive maxid as (
select max(id) as id from Mytable)
, cte as (
select 0 as rid
union all
select rid + 1
from cte
cross join maxid
where (rid + 1) * 2 <= maxid.id)
select max(case when m.id % 2 = 0 then name end) as even,
max(case when m.id % 2 = 1 then name end) as odd
from cte
left join Mytable m on floor(m.id / 2) = cte.rid
group by rid order by rid;
Result:
even | odd
:------ | :-----
null | one
two | three
four | null
six | null
null | null
null | eleven
null | null
null | null
sixteen | null
Demo: db fiddle
Credits: Thanks to #Nick, #Madhur Bhaiya for the fiddle and the logic used to create this query.
Here's a CTE based query that will work in SQL Server and MySQL > v8.0 (with the addition of the keyword recursive before maxid). It generates a list of rows that encompasses the pairs of MyTable values (in the sample, this is 0,1,2,3) and then JOINs that to Mytable to extract the even/odd column values:
with maxid as (
select max(id) as id from Mytable)
, cte as (
select 0 as rid
union all
select rid + 1
from cte
cross join maxid
where (rid + 1) * 2 <= maxid.id)
select max(case when m.id % 2 = 0 then name end) as even,
max(case when m.id % 2 = 1 then name end) as odd
from cte
join Mytable m on m.id / 2 = cte.rid
group by rid
Output:
even odd
one
two three
four
six
Demo on dbfiddle
I'm building a system that should show when the students missed two days in a row.
For example, this table contains the absences.
day | id | missed
----------------------------------
2016-10-6 | 1 | true
2016-10-6 | 2 | true
2016-10-6 | 3 | false
2016-10-7 | 1 | true
2016-10-7 | 2 | false
2016-10-7 | 3 | true
2016-10-10 | 1 | false
2016-10-10 | 2 | true
2016-10-10 | 3 | true
(days 2016-10-8 and 2016-10-9 are weekend)
in the case above:
student 1 missed the days 1st and 2nd. (consecutive)
student 2 missed the days 1st and 3rd. (nonconsecutive)
student 3 missed the days 2nd and 3rd. (consecutive)
The query should select only student 1 and 3.
Is possible to do stuff like this just with a single SQL Query?
Use inner join to connect two instances of the table- one with the 'first' day, and one with the 'second' day, and then just look for rows where both are missed:
select a.id from yourTable as a inner join yourTable as b
on a.id = b.id and a.day = b.day-1
where a.missed = true and b.missed = true
EDIT
Now that you changed the rules... and made it date and not int in the day column, this is what I'll do:
Use DAYOFWEEK() function to go to a day as a number
Filter out weekends
use modulo to get Sunday as the next day of Thursday:
select a.id from yourTable as a inner join yourTable as b
on a.id = b.id and DAYOFWEEK(a.day) % 5 = DAYOFWEEK(b.day-1) % 5
where a.missed = true and b.missed = true
and DAYOFWEEK(a.day) < 6 and DAYOFWEEK(b.day) < 6
similar approach as other answers, but different syntax
select distinct id
from t
where
missed=true and
exists (
select day
from t as t2
where t.id=t2.id and t.day+1=t2.day and t2.missed=true
)
This will give you each instance of it happening. You'll get multiple hits if they have 3 or more consecutive days missed, so if that's an issue then you'll need to refine it.
SELECT
T1.id,
T1.day
FROM
My_Table T1
INNER JOIN My_Table T2 ON
T2.id = T1.id AND
T2.day = T1.day + 1 AND
T2.missed = true
WHERE
T1.missed = true
You can do it using variables:
SELECT DISTINCT id
FROM (
SELECT day, id, missed,
#rn := IF(#id = id,
IF(missed = true, #rn + 1, 0),
IF(#id := id,
IF(missed = true, 1, 0),
IF(missed = true, 1, 0))) AS rn
FROM mytable
CROSS JOIN (SELECT #rn := 0, #id := 0) AS var
ORDER BY id, day) AS t
WHERE t.rn >= 2
The benefit of the above method is that it is scalable: it can be easily extended to check for more than 2 consecutive records of student absences.
Demo here
Here's one way...
SELECT x.id
FROM my_table x
JOIN my_table y
ON y.id = x.id
AND y.missed = x.missed
AND y.day > x.day
AND 5 * (DATEDIFF(y.day, x.day) DIV 7) + MID('0123444401233334012222340111123400001234000123440', 7 * WEEKDAY(x.day) + WEEKDAY(y.day) + 1, 1) <= 1
WHERE x.missed = 1;
We have a log table where user processes log entries (success/failure/timeout) each time they run. For e.g.
+----+----------+---------+
| id | username | status |
+----+----------+---------+
| 1 | saqib | success |
| 2 | mary | timeout |
| 3 | scott | timeout |
| 4 | saqib | success |
| 5 | mary | timeout |
| 6 | scott | timeout |
| 7 | saqib | timeout |
| 8 | mary | timeout |
| 9 | scott | timeout |
+----+----------+---------+
We would like to get a usernames which have had a success in the past the but the latest entry for them was a timeout. (saqib in the above example)
Is there single query that can do this? Right now we are doing this using a PHP script, but would like to use mySQL query for this.
Thanks
SQL Fiddle
SELECT DISTINCT m1.username
FROM
(
SELECT s1.username, s1.ids
FROM
(
SELECT username, MAX(id) as ids
FROM MyTable
GROUP BY username
) AS s1
INNER JOIN MyTable AS s2
ON s1.ids = s2.id
WHERE s2.status = 'timeout'
) AS m1
INNER JOIN MyTable m2 ON m1.username = m2.username
AND m2.status = 'success'
You can retrieve the latest id for each username and then JOIN it with the original table checking if there were entries for each user with status success and id less then maximum.
SELECT t.*
FROM ( SELECT username
, MAX(id) as ind
FROM tbl
GROUP BY username
) x JOIN tbl t ON t.username = x.username
AND t.id = x.ind
AND t.status IN ('timeout', 'failure')
AND EXISTS ( SELECT *
FROM tbl
WHERE username = x.username
AND id < x.ind
AND status = 'success'
)
Example
I would use exists for this problem. Exists are nice because they generally are faster than joining to the table. Unrelated, but I would recommend using a time stamp as opposed to relying on the id number.
Select username
From table t1
Where t1.status = 'timeout'
and exists (Select 1
From table t2
Where t1.username = t2.username
and t2.status = 'success'
Limit 1)
and not exists (Select 1
From table t3
Where t3.username = t1.username
and t3.id > t1.id
Limit 1);
SELECT
UserName, max(id)
FROM TABLE
WHERE
UserName IN (SELECT UserName from Table where Status = 'Success')
GROUP BY UserName
Having MAX(id) = (select max(id) from username where status = 'timeout')
You can join the table to itself:
SELECT GROUP_CONCAT(CONCAT_WS(' -> ', a.id, b.id) SEPARATOR ','), a.username
FROM t a
JOIN t b USING (username)
WHERE b.id > a.id
AND (a.status = 'success'
AND b.status = 'timeout')
GROUP BY a.username;
This shows all pairs of previous success to later timeout.
SQLFiddle
You can do so by joining 2 subqueries 1 for the maximum id per user with success status which will satisfy the condition which have had a success in the past and 2 for the max id to get users with latest timeout last comparison part t1.id < t2.id will satisfy the user should have success in past
select * from
(select `username`,max(id) id
from t
where `status` ='success'
group by `username`
) t1
join
(
select `username`,max(id) id
from t
where `status` ='timeout'
group by `username`
) t2
on(t1.username = t2.username)
where t1.id < t2.id
Demo
Another solution this will be much cleaner bu just using one query with max and case
select
username,
max(case when `status` ='success' then id else 0 end) success,
max(case when `status` ='timeout' then id else 0 end) timeout
from t
group by username
having timeout > success
and success > 0
Demo 2
I have a table like this:
client msg_type msg_body id
------ -------- -------- ---
123 typeA success abc
123 typeB success abc
456 typeA success abc
456 typeB failure abc
123 typeA success abc
123 typeA success abc
789 typeA success def
789 typeB success def
etc.
I would like output like this:
client diff id
------ ---- ---
123 2 abc
456 1 abc
789 0 def
where diff is the count of typeA:success messages - typeB:success messages. I can get the count of the typeA success using something like:
select client, count(*) from mytable
where msg_type="typeA" and msg_body="success"
However, I can't figure out how to put another count in there (for typeB) and also subtract.
I tried something like:
select client, count(*) from mytable
where msg_type="typeA" and msg_body="success" - count(*)
from mytable where msg_type="typeB" and msg_body="success"
But of course it didn't work, or I wouldn't be asking here. :) Any advice?
Edit: added another column. I tried the two suggestions given, but it only seems to return the results for one of the ids, not both.
Edit #2: I tried wrapping the SELECT query with:
select id, count(*) from (select ...) as anothertable where count_a_minus_count_b = 0;
I was hoping the output would be like:
id count
--- -----
abc 2
def 1
where count is the number of clients where the difference between typeA:success and typeB:success is 0.
COUNT counts non-null values, so you can construct an expression that's non-null when msg_type = 'typeA', and an expression that's non-null when msg_type = 'typeB'. For example:
SELECT client,
COUNT(CASE WHEN msg_type = 'typeA' THEN 1 END) AS count_a,
COUNT(CASE WHEN msg_type = 'typeB' THEN 1 END) AS count_b,
COUNT(CASE WHEN msg_type = 'typeA' THEN 1 END)
- COUNT(CASE WHEN msg_type = 'typeB' THEN 1 END) AS count_a_minus_count_b
FROM mytable
WHERE msg_body = 'success'
GROUP
BY client
;
(Disclaimer: not tested.)
Another way:
SELECT
d.client, COALESCE(a.cnt, 0) - COALESCE(b.cnt, 0) AS diff, d.id
FROM
( SELECT DISTINCT client, id
FROM mytable
) AS d
LEFT JOIN
( SELECT client, COUNT(*) AS cnt, id
FROM mytable
WHERE msg_type = 'typeA'
AND msg_body = 'success'
GROUP BY client, id
) AS a
ON d.client = a.client
AND d.id = a.id
LEFT JOIN
( SELECT client, COUNT(*) AS cnt, id
FROM mytable
WHERE msg_type = 'typeB'
AND msg_body = 'success'
GROUP BY client, id
) AS b
ON d.client = b.client
AND d.id = b.id ;
Tested at SQL-Fiddle
Here you go:
select client,
(sum(case when msg_type='typeA' and msg_body='success' then 1 else 0 end) -
sum(case when msg_type='typeB' and msg_body='success' then 1 else 0 end)) as diff
from your_table
group by client
Here's one way to get the result:
SELECT t.client
, SUM(t.msg_type<=>'typeA' AND t.msg_body<=>'success')
- SUM(t.msg_type<=>'typeB' AND t.msg_body<=>'success') AS diff
FROM mytable t
GROUP BY t.client
(The expressions in this query are MySQL specific; for a more portable query, use a less concise CASE expression to obtain an equivalent result.)
As more terse and obfuscated alternative to return the same result:
SELECT t.client
, SUM((t.msg_body<=>'success')*((t.msg_type<=>'typeA')+(t.msg_type<=>'typeB')*-1)) AS diff
FROM mytable t
GROUP BY t.client
I have a table call production
factory_id | factory_name | product_id
1 | A | 1
1 | A | 2
1 | A | 3
2 | B | 3
3 | C | 1
3 | C | 2
3 | C | 3
3 | C | 4
3 | C | 5
I'm trying to develop a query that will return two factory name pair such that every product of factory1 is produced by factory2, result looked like:
factory_name_1 | factory_name_2
A | C
B | A
B | C
I have some nested self join and renames, but I can't wrap my head around how I can apply EXISTS or IN for this scenario that does "for each product produced by factory X do condition". Thanks to any help in advanced.
Update:
Sorry that I forgot to paste my query:
select t0.fname0, t1.fname1
from (
select factory_id as fid0, factory_name as fname0, product_id as pid0, count(distinct factory_id, product_id) as pnum0
from production
group by factory_id
) t0
join
(
select factory_id as fid1, factory_name as fname1, product_id as pid1, count(distinct factory_id, product_id) as pnum1
from production
group by factory_id
) t1
where t0.fid0 <> t1.fid1
and t0.pnum0 < t1.pnum1
and t0.pid0 = t1.pid1;
Update 2: production is the only table. Expected output factory1 and factory2 are just the rename of factory_name attribute.
You need to JOIN the table for each factory pairing to make sure they "join" on the same product_ids, otherwise you might end up with similar counts for DISTINCT product_ids but these will not necessarily refer to the same product_ids.
This is my take on it:
SELECT bfna,afna, pcnt FROM (
SELECT a.factory_name afna, b.factory_name bfna, COUNT(DISTINCT b.product_id) commoncnt
FROM tbl a LEFT JOIN tbl b ON b.factory_name!=a.factory_name AND b.product_id=a.product_id
GROUP BY a.factory_name, b.factory_name
) c
INNER JOIN (
SELECT factory_name fna, COUNT(DISTINCT product_id) pcnt
FROM TBL GROUP BY factory_name
) d ON fna=bfna AND commoncnt=pcnt
ORDER BY bfna,afna
You can find a demo here: https://rextester.com/JJGCK84904
It produces:
bfna afna commoncnt
A C 3
B A 1
B C 1
For simplicity I left out the column factory_id as it does not add any information here.
Fun fact: as I am using only "bare-bone" SQL expressions, the above code will run on SQL-Server too without any changes.
You can do it this way:
select A as factory_name_1 , B as factory_name_2
from
(
select A, B, count(*) as Count_
from
(
select a.factory_name as A, b.factory_name as B
from yourtable a
inner join yourtable b
on a.product_id = b.product_id and a.factory_id <> b.factory_id
)a group by A, B
)a
inner join
(select factory_name, count(*) as Count_ from yourtable group by factory_name) b
on a.A = b.factory_name and a.Count_ = b.Count_
Order by 1
Output:
factory_name_1 factory_name_2
A C
B A
B C
The other solutions just seem more complicated than necessary. This is basically a self-join with aggregation:
with t as (
select t.*, count(*) over (partition by factory_id) as cnt
from tbl t
)
select t1.factory_id, t2.factory_id, t1.factory_name, t2.factory_name, count(*)
from t t1 join
t t2
on t1.product_id = t2.product_id and t1.factory_id <> t2.factory_id
group by t1.factory_id, t2.factory_id, t1.factory_name, t2.factory_name, t1.cnt
having count(*) = max(t1.cnt);
Here is a db<>fiddle.