Mysql query random order (pause and continue) question - mysql

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

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

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.

Condensing MySQL ordered group values

This question is an extension of mysql compress order_by values.
My table has groups of ordered numbers, with undesired gaps. How can I renumber each of these groups, while keeping the original order?
Group Order Desired Order
A 1 1
A 3 2
A 6 3
A 7 4
B 2 1
B 3 2
B 8 3
C 1 1
C 7 2
C 8 3
You can also get the desired order by using a correlated sub query without using and variables
select t1.*,
(
select count(*)
from demo t2
where t1.`group` = t2.`group`
and t1.`order` > t2.`order`
) + 1 desiredorder
from demo t1
DEMO
Or to update same table with your desired order you can use below query
update demo a
join (select t1.*,(
select count(*)
from demo t2
where t1.`group` = t2.`group`
and t1.`order` > t2.`order`
) + 1 desiredorder
from demo t1
) b on a.`group` = b.`group`
and a.`order` = b.`order`
set a.`order` = b.desiredorder
DEMO
Note make sure to add an index on group and order column for better performance.
You can create a variable to store the previous value of group and another one for the desiredorder. Essentially this is a mimic of a Window Function ROW_NUMBER which MySql doesn't support.
I renamed the columns as group=col1, order=col2
select col1,
col2,
neworder
from (
select t.col1,
t.col2,
case when #prev=t.col1 then #id:=#id+1 else #id:=1 end as neworder,
#prev:=t.col1
from (select #prev:=null, #id:=0) a,
toorder t
order by t.col1
) a
See it working here: http://sqlfiddle.com/#!9/9d8528/2
The external query is only needed if you want to pull out only those three columns.

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

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.

Need a sequence number for every row in MySQL query

So I found this great use:
SELECT (#row:=#row+1) AS ROW, ID
FROM TableA ,(SELECT #row := 0) r
ORDER BY ID DESC
The #row:=#row+1 works great, but I get the row ordered by the ID.
My tables look more like this:
SELECT (#row:=#row+1) AS ROW, ID , ColA, ColB, ColC
FROM TableA
JOIN TableB on TableB.ID = TableA.ID
JOIN TableC on TableC.ID = TableA.ID
WHERE ID<500
,(SELECT #row := 0) r
ORDER BY ID DESC
Note:
I noticed that if I remove the JOINs I DO get the requested result (In Which ROW is the sequential number of each row, no matter the ORDER BY of ID). The first example works great but for some reaosn, the JOINs mess it up somehow.
so I get this:
ROW | ID
3 15
2 10
1 2
What I am after is:
ROW | ID
1 15
2 10
3 2
Here's the SqlFiddle
So it basically seems that the row number is evaluated before the ORDER BY takes place. I need the ORDER BY to take place after row was given.
How can I achieve that?
Remove the ORDER BY:
SELECT (#row:=#row+1) AS ROW, ID
FROM table1 ,(SELECT #row := 0) r
See SQL Fiddle with Demo
Then if you want to use an ORDER BY wrap the query in another SELECT:
select *
from
(
SELECT (#row:=#row+1) AS ROW, ID
FROM table1 ,(SELECT #row := 0) r
) x
order by row
Or if you leave the ORDER BY on the query, then you can see the way the row number is being applied by simply playing with either DESC or ASC order - See Demo
If you use DESC order
SELECT (#row:=#row+1) AS ROW, ID
FROM table1, (SELECT #row := 0) r
order by id desc;
the results are which appears to be the result you want:
ROW | ID
----------
1 | 15
2 | 10
3 | 2
If you use ASC order:
SELECT (#row:=#row+1) AS ROW, ID
FROM table1 ,(SELECT #row := 0) r
ORDER BY ID;
the results are:
ROW | ID
----------
1 | 2
2 | 10
3 | 15
Edit, based on your change, you should place the row number in a sub-query, then join the other tables:
select *
from
(
SELECT (#row:=#row+1) AS ROW, ID
FROM Table1,(SELECT #row := 0) r
order by ID desc
) x
JOIN Table2
on x.ID = Table2.ID;
See SQL Fiddle with Demo
I dont find any problem with your query
SELECT (#row:=#row+1) AS ROW, ID
FROM table1 ,(SELECT #row := 0) r
order by ID desc
SQL Fiddle demo