MySQL - select rows with limit - mysql

How can I select a certain number of rows, based on a condition (using IN or any other conditional), and if the number of rows returned by the condition is less than LIMIT x then to select from the remainig rows until the LIMIT x is met?
So, if I have the following table:
id comment ord
1 ... null
2 ... 1
3 ... 2
4 ... null
then the result set should be, using LIMIT 3:
id comment ord
2 ... 1
3 ... 2
1 ... null
If the ord column is not null, then I want to select the respective row(s), if not, I want to select one or more from the rest of the rows, having ord NULL, until the LIMIT 3 condition is reached.
Another example, if I have the next table data:
id comment ord
1 ... 3
2 ... 1
3 ... 2
4 ... null
Then the result set should be
id comment ord
2 ... 1
3 ... 2
1 ... 3
I have tried this mysql code:
SELECT t.* FROM table t
WHERE
t.ord IN (SELECT t1.ord FROM table t1 WHERE t1.ord IS NOT NULL ORDER BY t1.ord ASC)
OR
t.id IN (SELECT t2.id FROM table t2 WHERE t2.ord IS NULL ORDER BY t2.id ASC)
LIMIT 3;
But I always get the rows that have ord NULL, even if I have some ord columns not null.
Any help please?

Put the check for whether ord is null in your ORDER BY:
SELECT *
FROM table
ORDER BY ord IS NULL, ord, id
LIMIT 3
ord IS NULL will be 0 for non-null ord, 1 for null ord, so this will put all the non-null rows first. Within those it will order by ord, then the remainder will be ordered by id.
Then LIMIT 3 will select the first 3 of all of these.

Related

How i can get all query in one field?

I've two tables like this:
First :
id
num
1
a
2
b
3
c
Second:
id
first_id
value
11
1
a1
12
1
a2
13
1
a3
And I need to get result like this:
id
value
1
a1-a2-a3
I've tried with query:
SELECT first.id, (SELECT second.value FROM second
WHERE second.first_id = first.id) AS value
FROM first
But I've got #1242 error. How I can do it?
You can use group_concat()
select first_id as id,
group_concat(`value` order by `value` separator '-') as combined_values
FROM second_table
group by first_id

How to find duplicate and update column value of all but the most recent entry

I have a table items with columns item_id, lockup_id, date, archive. I need to be able to go through the lookup_id column and identify duplicates, changing the archive value to 1 on every duplicate EXCEPT the newest entry in the table.
item_id Lookup_id date archive
------------------------------------------------
1234 4 1-1-19 0
1235 4 1-1-19 0
1236 4 1-1-19 0
1237 2 1-1-19 0
1238 1 1-1-19 0
1239 1 1-1-19 0
I've so far managed to find the duplicates using the following statement, but I'm at a bit of a loss where to go with this to achieve my desired result.
'SELECT `item_id` , `lookup_id`, `date`, `archive`
FROM items
WHERE `item_id`
IN (
`SELECT `item_id`
FROM items
GROUP BY `item_id`
HAVING COUNT( `item_id` ) >1
)
ORDER BY `item_id`;
You can do it in two steps.
First set all values in archive to 1:
update items set archive = 1 where 1;
Then set archive = 0 for "newest" entries only:
update items i
inner join (
select max(item_id) as item_id
from items
group by Lookup_id
) x using(item_id)
set i.archive = 0;
You will get the following result:
item_id Lookup_id date archive
1234 4 1-1-19 1
1235 4 1-1-19 1
1236 4 1-1-19 0
1237 2 1-1-19 0
1238 1 1-1-19 1
1239 1 1-1-19 0
This method should be quite efficient with an index on (Lookup_id, item_id).
Demo
Looks like item_id is sequential, assuming newest entry has the highest item_id you could filter for the highest item_id for each lookup_id and then update all records except these.
update items set archive = 1
where item_id not in
(
select max(item_id) from items
group by lookup_id
);
In order to archive all the items with the same lookup_id except the newer one you can use this sql statement
UPDATE tn SET tn.archive = 1
FROM table_name tn
WHERE (SELECT COUNT(tn2.id) FROM table_name AS tn2 WHERE tn2.lookup_id = tn.lookup_id) > 1
AND tn.id NOT IN (SELECT tn2.id FROM table_name AS tn2 WHERE tn2.lookup_id = tn.lookup_id ORDER BY tn.date DESC, tn.id DESC LIMIT 1);
In the where conditions first we check if more than one item with the same lookup_id exists and then we check that the actual item is not the newer of all the items with the same lookup_id.
Looking at you example I am assuming the latest entry is the one with highest item id
if that's the case you can create a CTE with a column and use row number / partition by
Something like this - the join will change depending on which columns are unique
;WITH cte_test AS
(SELECT item_id , lookup_id , ROW_NUMBER() OVER (PARTITION BY lookup_id ORDER BY item_id ) AS rn
FROM items )
UPDATE it2
SET it2.archive = 1
FROM items it2
INNER JOIN cte_test ct
ON ct.item_id = it2.item_id
AND ct.lookup_id = it2.lookup_id where rn > 1

Mysql only get newest record where columns are set and get the empty columns from the next row

I have a MySQL database table with many columns that can be empty (null).
I would like in one result row the newest value for every column for one machine. So, if null, the value will be of one older row, like illustrated below.
Table content:
id name motorSpeed motorDelay F1type F2type F3type
1 001 200 null 1 null null
2 002 400 0 0 0 0
3 001 null 50 0 1 1
Desired result:
id name motorSpeed motorDelay F1type F2type F3type
3 001 200 50 0 1 1
What would be the best query to get this aggregated result?
I suspect name is the key and it can not be null. Also that you want to get the 'latest' values for name=001.
SELECT a.id, b.name, c.motorSpeed, d.motorDelay, e.F1type, f.F2type, g.F3type
FROM
(SELECT * FROM table WHERE id IS NOT NULL ORDER BY id DESC LIMIT 1) a
LEFT JOIN
(SELECT * FROM table WHERE name IS NOT NULL ORDER BY id DESC LIMIT 1) b ON b.name=a.name
LEFT JOIN
(SELECT * FROM table WHERE motorSpeed IS NOT NULL ORDER BY id DESC LIMIT 1) c ON c.name=a.name
LEFT JOIN
(SELECT * FROM table WHERE motorDelay IS NOT NULL ORDER BY id DESC LIMIT 1) d ON d.name=a.name
LEFT JOIN
(SELECT * FROM table WHERE F1type IS NOT NULL ORDER BY id DESC LIMIT 1) e ON e.name=a.name
LEFT JOIN
(SELECT * FROM table WHERE F2type IS NOT NULL ORDER BY id DESC LIMIT 1) f ON f.name=a.name
LEFT JOIN
(SELECT * FROM table WHERE F3type IS NOT NULL ORDER BY id DESC LIMIT 1) g ON g.name=a.name
WHERE a.name='001';
SELECT ID,
NAME,
CASE WHEN MOTORSPEED IS NULL
THEN MIN(MOTORSPEED)
ELSE MOTORSPEED
END AS MOTORSPEED,
CASE WHEN MOTORDELAY IS NULL
THEN MAX(MOTORDELAY)
ELSE MOTORDELAY
END AS MOTORDELAY,
CASE WHEN F1TYPE IS NULL
THEN MAX(F1TYPE)
ELSE F1TYPE
END AS F1TYPE,
CASE WHEN F2TYPE IS NULL
THEN MAX(F2TYPE)
ELSE F2TYPE
END AS F2TYPE,
CASE WHEN F3TYPE IS NULL
THEN MAX(F3TYPE)
ELSE F3TYPE
END AS F3TYPE
FROM YOURTABLE;

MySQL- Select at least n rows per group

Suppose you have
id / value
1 2
1 3
1 6
2 3
3 1
3 3
3 6
And I want to retrieve at least n rows per id group, let's say n = 4. In addition, it would help if a counter is added as a column. So the results should be like:
counter / id / value
1 1 2
2 1 3
3 1 6
4 null null
1 2 3
2 null null
3 null null
4 null null
1 3 1
2 3 3
3 3 6
4 null null
regards
I'm assuming that the combination of id and value is unique. Here's how you can do it without using MySQL variables:
SELECT
a.n AS counter,
b.id,
b.value
FROM
(
SELECT
aa.n,
bb.id
FROM
(
SELECT 1 AS n UNION ALL
SELECT 2 AS n UNION ALL
SELECT 3 AS n UNION ALL
SELECT 4 AS n
) aa
CROSS JOIN
(
SELECT DISTINCT id
FROM tbl
) bb
) a
LEFT JOIN
(
SELECT aa.id, aa.value, COUNT(*) AS rank
FROM tbl aa
LEFT JOIN tbl bb ON aa.id = bb.id AND aa.value >= bb.value
GROUP BY aa.id, aa.value
) b ON a.id = b.id AND a.n = b.rank
ORDER BY
a.id,
a.n
The next blog post describes the solution to your query:
SQL: selecting top N records per group.
It requires an additional small table of numbers, which is utilized to "iterate" the top N values per group via String Walking technique.
It uses GROUP_CONCAT as a way to overcome the fact MySQL does not support Window Functions. This also means it's not a pretty sight!
An advantage of this technique is that it does not require subqueries, and can optimally utilize an index on the table.
To complete the answer to your question, we must add an additional columns: you have requested a counter per item per group.
Here's an example using the world sample database, choosing top 5 largest counties per continent:
CREATE TABLE `tinyint_asc` (
`value` tinyint(3) unsigned NOT NULL default '0',
PRIMARY KEY (value)
) ;
INSERT INTO `tinyint_asc` VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15),(16),(17),(18),(19),(20),(21),(22),(23),(24),(25),(26),(27),(28),(29),(30),(31),(32),(33),(34),(35),(36),(37),(38),(39),(40),(41),(42),(43),(44),(45),(46),(47),(48),(49),(50),(51),(52),(53),(54),(55),(56),(57),(58),(59),(60),(61),(62),(63),(64),(65),(66),(67),(68),(69),(70),(71),(72),(73),(74),(75),(76),(77),(78),(79),(80),(81),(82),(83),(84),(85),(86),(87),(88),(89),(90),(91),(92),(93),(94),(95),(96),(97),(98),(99),(100),(101),(102),(103),(104),(105),(106),(107),(108),(109),(110),(111),(112),(113),(114),(115),(116),(117),(118),(119),(120),(121),(122),(123),(124),(125),(126),(127),(128),(129),(130),(131),(132),(133),(134),(135),(136),(137),(138),(139),(140),(141),(142),(143),(144),(145),(146),(147),(148),(149),(150),(151),(152),(153),(154),(155),(156),(157),(158),(159),(160),(161),(162),(163),(164),(165),(166),(167),(168),(169),(170),(171),(172),(173),(174),(175),(176),(177),(178),(179),(180),(181),(182),(183),(184),(185),(186),(187),(188),(189),(190),(191),(192),(193),(194),(195),(196),(197),(198),(199),(200),(201),(202),(203),(204),(205),(206),(207),(208),(209),(210),(211),(212),(213),(214),(215),(216),(217),(218),(219),(220),(221),(222),(223),(224),(225),(226),(227),(228),(229),(230),(231),(232),(233),(234),(235),(236),(237),(238),(239),(240),(241),(242),(243),(244),(245),(246),(247),(248),(249),(250),(251),(252),(253),(254),(255);
SELECT
Continent,
SUBSTRING_INDEX(
SUBSTRING_INDEX(
GROUP_CONCAT(Name ORDER BY SurfaceArea DESC),
',', value),
',', -1)
AS Name,
CAST(
SUBSTRING_INDEX(
SUBSTRING_INDEX(
GROUP_CONCAT(SurfaceArea ORDER BY SurfaceArea DESC),
',', value),
',', -1)
AS DECIMAL(20,2)
) AS SurfaceArea,
CAST(
SUBSTRING_INDEX(
SUBSTRING_INDEX(
GROUP_CONCAT(Population ORDER BY SurfaceArea DESC),
',', value),
',', -1)
AS UNSIGNED
) AS Population,
tinyint_asc.value AS counter
FROM
Country, tinyint_asc
WHERE
tinyint_asc.value >= 1 AND tinyint_asc.value <= 5
GROUP BY
Continent, value
;

Complex SQL Querying: Two queries within the same table?

I have a table like:
ID | LABEL | SOME_VALUE
1 a rand_1
2 a NULL
3 b rand_9
4 c rand_3
5 c rand_3
6 c rand_3
7 d NULL
8 d rand_4
As you can see, ID is unique, label is not unique (can be 1 or more) and some_value is also not unique.
What I want to do is the following:
I want to get a unique list of LABELS, which exist in the database in more than one rows (min 2) and of which rows has SOME_VALUE not NULL.
So I would get:
ID | LABEL | SOME_VALUE
1 a rand_1
2 a NULL
7 d NULL
8 d rand_4
in return.
How can I achieve this?
There are two versions. First one does exactly as listed in results, eliminating rand_3 because even though it appears three times all the values are the same (I don't see distinct condition specified in question).
There must be a better way, but as they say I can't brain today, I have the dumb :-)
select *
from tbl
inner join
(
select label
FROM tbl
GROUP BY Label
HAVING count (distinct some_value)
+ sum(distinct case when some_value is null then 1 else 0 end) > 1
) a
on tbl.label = a.label
Second one retrieves C also following the requirements (some_value being not null for at least one of some_value).
select *
from tbl
inner join
(
select label
FROM tbl
GROUP BY Label
HAVING count(*) > 1 and count(some_value) > 0
) a
on tbl.label = a.label
And there is Sql Fiddle.
The HAVING parameter limits grouped items:
SELECT
Label
FROM dbo.TableName
WHERE NOT Some_Value IS NULL
GROUP BY Label
HAVING COUNT(*) > 2
SELECT t1.*
FROM yourTable t1
JOIN yourTable t2
ON t1.LABEL = t2.LABEL
AND t1.ID < t2.ID
WHERE t1.SOME_VALUE IS NOT NULL
OR t2.SOME_VALUE IS NOT NULL
This should work -
SELECT test.*
FROM (
SELECT label
FROM test
GROUP BY Label
HAVING COUNT(DISTINCT IFNULL(some_value, '~null~')) > 1
) AS tmp
INNER JOIN test
ON tmp.label = test.label;