find 4 consecutive rows with a specific value using a mysql query - mysql

My table has 4 columns: id (autoincrement), machine (integer between 1 and 300), event (integer), status (string)
I need to run a query on this table, that returns the four machines with the lowest possible machine number where event="5" and status = "free" and that are consecutive numbers.
for example if machine 3 is busy, the query should NOT return 1,2,4,5 because they are not consequent. instead if machines 4,5,6,7 are free it should return those. It should NOT return 5,6,7,8 since it is not the lowest possible machinenumber. the lowest possible is 4,5,6,7.
select * from mytable where event="5" and status="free" order by machine asc limit 4
does exactly what i need, except that it does return all rows, not taking into account that they MUST be consequent in the machines column.
Can this be done?
sample data as requested:
id - machine - event - status
22 1 5 free
23 2 5 free
24 3 5 busy
25 4 5 busy
26 5 5 free *
27 6 5 free *
28 7 5 free *
29 8 5 free *
30 9 5 free
31 10 5 busy
32 11 5 free
The lines marked with * are the lines i would need to get. the first 4 consecutive rows on column machine, with status value free and event id = 5.

Something like this should work
SELECT SUBSTRING_INDEX(GROUP_CONCAT(machine ORDER BY machine),',',4) as machines FROM
(SELECT machine,
CASE WHEN machine=#machine + 1 THEN #n ELSE #n:=#n+1 END AS g,
#machine := machine As m
FROM
t, (SELECT #n:=0) r, (SELECT #machine := '') z
WHERE event=5 and status="free"
ORDER BY
id) sub
GROUP BY g
HAVING COUNT(*) >=4
If you want ALL the rows
SELECT t.id,t.machine,t.event,t.status FROM
(SELECT id,machine,event,status,GROUP_CONCAT(id ORDER BY id) gr FROM
(SELECT *,
CASE WHEN machine=#machine + 1 THEN #n ELSE #n:=#n+1 END AS g,
#machine := machine As m
FROM
t, (SELECT #n:=0) r, (SELECT #machine := '') z
WHERE event=5 and status="free"
ORDER BY
id) sub
GROUP BY g
HAVING COUNT(*) >=4) o
JOIN t ON FIND_IN_SET(t.id,gr)
ORDER BY id LIMIT 4
Test
Test2

If I understand correctly, this should do the trick
select t1.id, t2.id, t3.id, t4.id
from (select * from mytable where event="5" and status="free") t1
join (select * from mytable where event="5" and status="free") t2
on t1.id + 1 = t2.id
join (select * from mytable where event="5" and status="free") t3
on t1.id + 2 = t3.id
join (select * from mytable where event="5" and status="free") t4
on t1.id + 3 = t4.id
order by t1.id
limit 1
May require some tweaking on the join conditions (hard to tell without examples).

this depends a little on the size of your dataset, but for small datasets this should work:
select * from mytable m1 where m1.event="5" and m1.status="free"
and(m1.machine-1 in (select machine from mytable where event="5" and status="free")
and m1.machine-2 in (select machine from mytable where event="5" and status="free")
and m1.machine-3 in (select machine from mytable where event="5" and status="free")
OR( m1.machine+1 in (select machine from mytable where event="5" and status="free")
and m1.machine+2 in (select machine from mytable where event="5" and status="free")
and m1.machine+3 in (select machine from mytable where event="5" and status="free") )
order by m1.machine asc limit 4
for larger datasets, you'll want to use a JOIN to accomplish the same.
you can also just use the second half to find the first instance and then start your query from there.

Related

SQL add same contents

How can I delete all columns that are duplicate and don't have the biggest "amount". I have the following table:
ID TIME AMOUNT
-----------------------------------
1 x 5
2 y 1
2 y 3
3 z 1
3 z 2
3 z 3
But I want it to be like this, so that only the column which has the biggest number "survives":
ID TIME AMOUNT
------------------------------------
1 x 5
2 y 3
3 z 3
How can I do this?
You can get the max amount per id and time and then get the rows matching:
select t.Id, t.Time, t.amount
from myTable t
inner join
(select Id, time, max(amount) as amt
from myTable
group by Id, Time) tmp on t.id = tmp.id and
t.time = tmp.time and
t.amount = tmp.amt
DbFiddle demo
EDIT: You may want to add DISTINCT depending on your needs.
One other approach using a CTE
with del as (
select *,
First_Value(amount) over(partition by id order by amount desc) maxamount
from t
)
delete from t
using t join del on t.id = del.id and t.amount < maxamount;
WITH cte AS
(
SELECT
ID,
ROW_NUMBER() OVER (PARTITION BY TIME ORDER BY AMOUNT DESC) AS ROWNUM
FROM
MyTable
)
DELETE MyTable
FROM MyTable
JOIN cte USING (ID)
WHERE ROWNUM > 1;
WITH syntax requires MySQL 8.0.
I think some of the answers here are overly complicated.
delete t
from yourtable t
join yourtable t2 on t.id = t2.id
and t.time = t2.time
and t2.amount > t.amount

Sort records based on string

Please consider the table below
Id F1 F2
---------------------------
1 Nima a
2 Eli a
3 Arian a
4 Ava b
5 Arsha b
6 Rozhan c
7 Zhina c
I want to display records by sorting COLUMN F2 to display one record from each string category (a,b,c) in order
Id F1 F2
---------------------------
1 Nima a
5 Arsha b
6 Rozhan c
2 Eli a
4 Ava b
7 Zhina c
3 Arian a
NOTE: a,b,c could be anything... it should take one record from one entry and then 2nd from 2nd entry.
I have used join, or group by records but no success.
MySQL version 5.7 – Syed Saqlain
SELECT id, f1, f2
FROM ( SELECT t1.id, t1.f1, t1.f2, COUNT(*) cnt
FROM test t1
JOIN test t2 ON t1.f2 = t2.f2 AND t1.id >= t2.id
GROUP BY t1.id, t1.f1, t1.f2 ) t3
ORDER BY cnt, f2;
https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=8138bd9ab5be36ba534a258d20b2e555
ROW_NUMBER() alternative for lower version of MYSQL. This query will work for version 5.5, 5.6 & 5.7.
-- MySQL (v5.7)
SELECT t.id, t.f1, t.f2
FROM (SELECT #row_no:=CASE WHEN #db_names=d.f2 THEN #row_no+1 ELSE 1 END AS row_number
, #db_names:= d.f2
, d.f2
, d.f1
, d.id
FROM test d,
(SELECT #row_no := 0,#db_names:='') x
ORDER BY d.f2) t
ORDER BY t.row_number, t.f2
Please check from url https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=02dbb0086a6dd7c926d55a690bffbd06
You can use window functions in the order by:
select t.*
from t
order by row_number() over (partition by f2 order by id),
f2;
The row_number() function (as used above) assigns a sequential number starting with 1 to each value of f2.
In older versions of MySQL, you can use a correlated subquery instead:
order by (select count(*) from t t2 where t2.f2 = t.f2 and t2.id <= t.id),
f2;
For performance, you want an index on (f2, id).

Random elements inside JOIN

I have this code here
INSERT INTO Directory.CatalogTaxonomy (`CatalogId`, `TaxonomyId`, `TaxonomyTypeId`, `IsApprovalRelevant`)
SELECT cat.CatalogId, dep.Id, #department_type, false
FROM Directory.Catalog cat
JOIN (SELECT * FROM (
SELECT * FROM Taxonomy.Department LIMIT 10
) as dep_tmp ORDER BY RAND() LIMIT 3) AS dep
WHERE cat.CatalogId NOT IN (SELECT CatalogId FROM Directory.CatalogTaxonomy WHERE TaxonomyTypeId = #department_type)
AND cat.UrlStatus = #url_status_green
AND (cat.StatusId = #status_published
OR cat.StatusId = #status_review_required);
And the problem is that, it should for each catalog take the first 10 elements from Department and randomly choose 3 of them, then add to CatalogDepartment 3 rows, each containing the catalog id and a taxonomy id. But instead it randomly chooses 3 Department elements and then adds those 3 elements to each catalog.
The current result looks like this:
1 000de9d7-af8b-4bac-bdbd-e6e361e5bc5e
1 001d4060-2924-4c75-b304-d780454f261b
1 001bc4b8-c1bc-498d-9aee-3825a40587d5
2 000de9d7-af8b-4bac-bdbd-e6e361e5bc5e
2 001d4060-2924-4c75-b304-d780454f261b
2 001bc4b8-c1bc-498d-9aee-3825a40587d5
3 000de9d7-af8b-4bac-bdbd-e6e361e5bc5e
3 001d4060-2924-4c75-b304-d780454f261b
3 001bc4b8-c1bc-498d-9aee-3825a40587d5
As you can see, there are only 3 departments chosen and repeated for every catalog
If you think that the query:
SELECT * FROM (
SELECT * FROM Taxonomy.Department LIMIT 10
) as dep_tmp
ORDER BY RAND() LIMIT 3
that you join to Directory.Catalog returns 3 different departments for each catalog then you are wrong.
This query is executed only once and returns 3 random departments which are joined (always the same 3) to Directory.Catalog.
What you can do is after you CROSS JOIN 10 departments to Directory.Catalog, choose randomly 3 of them for each catalog.
Try this:
INSERT INTO Directory.CatalogTaxonomy (`CatalogId`, `TaxonomyId`, `TaxonomyTypeId`, `IsApprovalRelevant`)
WITH cte AS (
SELECT cat.CatalogId, dep.Id AS TaxonomyId, #department_type AS TaxonomyTypeId, false AS IsApprovalRelevant
FROM Directory.Catalog AS cat
CROSS JOIN (SELECT * FROM Taxonomy.Department LIMIT 10) AS dep
WHERE cat.CatalogId NOT IN (SELECT CatalogId FROM Directory.CatalogTaxonomy WHERE TaxonomyTypeId = department_type)
AND cat.UrlStatus = #url_status_green
AND (cat.StatusId = #status_published OR cat.StatusId = #status_review_required);
)
SELECT t.CatalogId, t.TaxonomyId, t.TaxonomyTypeId, t.IsApprovalRelevant
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY CatalogId ORDER BY RAND()) rn
FROM cte
) t
WHERE t.rn <= 3
Note that this:
SELECT * FROM Taxonomy.Department LIMIT 10
does not guarantee that you get the first 10 elements from Department because a table is not ordered.

Mysql query configuration

I have this query and runs perfectly, i just want to short the results to help me for quick reading :
SELECT numbers
FROM vista
WHERE id IN (
SELECT b.id + 3 FROM(
SELECT t1.id, t1.numbers t1val, t2.numbers t2val
FROM vista t1
JOIN vista t2 ON t1.id = t2.id-1
JOIN vista t3 ON t1.id = t3.id-2
WHERE t1.id = (SELECT MAX(id) - 2 FROM vista)
) a
JOIN (
SELECT t1.id, t1.numbers t1val, t2.numbers t2val, t3.numbers t3val
FROM vista t1
JOIN vista t2 ON t1.id = t2.id-1
JOIN vista t3 ON t1.id = t3.id-2
WHERE t1.id < (SELECT MAX(id) - 2 FROM vista)
) b
ON a.t1val = b.t1val
AND a.t2val = b.t2val
AND a.t3val = b.t3val
AND a.id <> b.id
)
ORDER BY id;
And the results it is like :
3
5
2
7
5
5
7
3
How can i get results counted in group and percentage :
Like :
5 = 3 (37.50%)
7 = 2 (25.00%)
3 = 2 (25.00%)
2 = 1 (12.50%)
Well if your query works than you should do something like this to achieve result you want:
Solution one - this will give you numbers, countend numbers in group and percentage in seperate column...
SELECT numbers, COUNT(numbers), (COUNT(numbers) / (SELECT COUNT(numbers)
FROM vista
/*your WHERE clause*/)) * 100 AS percent
FROM vista
-- your WHERE Statement
GROUP BY numbers
-- your ORDER BY
Here is a sql fiddle to see how that's look like http://sqlfiddle.com/#!9/08b4e/3
Solution two - this will give you numbers in one column and counted numbers in group and percentage in other:
SELECT numbers, CONCAT(CountNum, ' (', percent, '%)') AS counted
FROM (SELECT numbers, COUNT(numbers) AS CountNum, (COUNT(numbers) / (SELECT COUNT(numbers)
FROM vista
WHERE ID BETWEEN 1 AND 8
/*your WHERE clause*/)) * 100 AS percent
FROM vista
WHERE ID BETWEEN 1 AND 8
-- your WHERE clause this is just for a testing purpose
GROUP BY numbers
ORDER BY ID) AS srcTable
-- your ORDER BY clause this is just for a testing purpose
Here is Fiddle for that http://sqlfiddle.com/#!9/51275/1
Of course you need to insert your WHERE clause instead of mine i just put this one to test it. GL i hope i help a little.

Mysql query random order (pause and continue) question

Okay i am trying to create a mysql query that does this:
show 3 random records from table then after the 3th record show TEXT
and then show the same 3 items but other field (equaling to the items ofcourse) from same table.
eg table info:
--ids | titles------
10 | one
20 | two
30 | three
and the query results from the given example:
30 10 20 TEXT three one two
if anyone understand what i am asking,post your suggestion/asnwer
thanks for your time all :)
Just for kicks..
select t1.id, t2.id, t3.id, 'TEXT', t1.title, t2.title, t3.title
FROM
(
select #r := #r + 1 rownum, id
from (select #r:=0) initvar, (
select id
from tbl
order by rand()
limit 3
) X
) Y
join tbl t1 on Y.rownum=1 and t1.id = Y.id
join tbl t2 on Y.rownum=2 and t2.id = Y.id
join tbl t3 on Y.rownum=3 and t3.id = Y.id
You should really just do the query below, and do whatever display processing using the 3 rows returned, in whatever programming environment you use (Java/PHP/.Net etc).
select id, title
from tbl
order by rand()
limit 3
EDIT
To get the data in 7 different rows, you can use the below. I stress again that this is front-end display code. I will not use such SQL code in a production system.
select display
from
(
select sorter, rownum,
case when sorter=3 then title else id end display
from
(
select #r := #r + 1 rownum, id, title
from (select #r:=0) initvar,
(
select id, title
from tbl
order by rand()
limit 3
) X
) Y, (select 1 sorter union all select 3) dup
union all
select 2, 0, 'TEXT'
) Z
order by sorter, rownum
Example Output
7
2
1
TEXT
test 7 << title for id=7
test 2
test 1