Mysql Date Comparison with IN Statement - mysql

I have an sql query
I have the following Queries
SELECT * FROM articles where `id` =1 AND `datatime` > ='datetime1';
UNION ALL
SELECT * FROM articles where `id` =2 AND `datatime` > ='datetime2';
UNION ALL
SELECT * FROM articles where `id` =3 AND `datatime` > ='datetime3';
UNION ALL
SELECT * FROM articles where `id` =4 AND `datatime` > ='datetime4';
Which is working fine
Now the problem is that if there is bigger list, maybe more than 10000, then how do I handle this query.
Is there is any other way to do this query?

Instead of unioning, you should do this in one query.
SELECT * FROM articles where
(`id` =1 AND `datatime` > ='datetime1')
or
(`id` =2 AND `datatime` > ='datetime2')
or
(`id` =3 AND `datatime` > ='datetime3')
or
(`id` =4 AND `datatime` > ='datetime4');
You can also do it like this:
SELECT * FROM articles where
(id, `datatime`) IN (SELECT 1, 'datetime1'
UNION ALL
SELECT 2, 'datetime2'
UNION ALL
SELECT 3, 'datetime3'
UNION ALL
SELECT 4, 'datetime4'
);
If the datatime value is always the same, you can do it like this:
SELECT * FROM articles where
id IN (1, 2, 3, 4)
and datatime = 'datetime_value';
If your list of values gets really big, it's best to put those values in a table first and join it.
SELECT * FROM articles a
INNER JOIN your_values_table yvt ON a.id = yvt.id AND a.datatime = yvt.datatime;

Related

MySQL - Add values to the IN clause?

I am currently have this query:
SET sql_mode='';
SELECT `id`, `url`,`number`,`abbrev`,`content`,`label`,`hier-1` FROM `leganalyse_unitsub_2020` WHERE `id` IN (SELECT MAX(`id`) FROM `leganalyse_unitsub_2020` GROUP BY `url`)
The subquery in the following clause:
SELECT MAX(`id`) FROM `leganalyse_unitsub_2020` GROUP BY `url`
returns a set of integer values.
For each integer value returned x, I'd like to also include x-1, x-2, x-3, x-4 and x-5.
So something like:
SELECT `id`, `url`,`number`,`abbrev`,`content`,`label`,`hier-1` FROM `leganalyse_unitsub_2020` WHERE `id` IN
(
SELECT MAX(`id`) FROM `leganalyse_unitsub_2020` GROUP BY `url`
UNION
SELECT MAX(`id`)-1 FROM `leganalyse_unitsub_2020` GROUP BY `url`
UNION
SELECT MAX(`id`)-2 FROM `leganalyse_unitsub_2020` GROUP BY `url`
UNION
SELECT MAX(`id`)-3 FROM `leganalyse_unitsub_2020` GROUP BY `url`
UNION
SELECT MAX(`id`)-4 FROM `leganalyse_unitsub_2020` GROUP BY `url`
)
But I am not sure if this is correct.
What query will do what I have described?
Cross join the subquery with a query that returns 0, 1, 2, 3, 4 to get all the possible values:
SELECT id, url,number, abbrev, content, label, hier-1
FROM leganalyse_unitsub_2020 WHERE id IN (
SELECT g.id - t.id
FROM (SELECT 0 id UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4) t
CROSS JOIN (SELECT MAX(id) id FROM leganalyse_unitsub_2020 GROUP BY url) g
)
This code is based on your last query.
If you want the values to be like x-1, x-2, x-3, x-4 and x-5 as you mention in the question the you should use this subquery:
SELECT 1 id UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5

MySql CASE Execute Query Returns Operand should contain 1 columns

The following code returns Operand should contain 1 columns.
SELECT
CASE WHEN
(SELECT COUNT(1) FROM `student` WHERE `join_date` > '2017-03-21 09:00:00') > 0
THEN
(SELECT * FROM `student` >= CAST(CAST('2017-03-21 09:00:00' AS DATE) AS DATETIME)
END
but the following works. Why?
SELECT
CASE WHEN
(SELECT COUNT(1) FROM `student` WHERE `join_date` > '2017-03-21 00:00:00') > 0
THEN
(SELECT `foo`)
ELSE
(SELECT `bar`)
END
How if i want to perform checking and execute 2 different queries according to the checking result.
I want to achieve following result (works fine in sql)
IF (SELECT COUNT(*) FROM table WHERE term LIKE "term") > 4000
EXECUTE (SELECT * FROM table1)
ELSE
EXECUTE (SELECT * FROM table2)
If you force your subselect tor return only a row the also the first select work
SELECT
CASE WHEN
(SELECT COUNT(1) FROM `student` WHERE `join_date` > '2017-03-21 00:00:00') > 0
THEN
(SELECT * FROM `student` order by your_column limit 1)
ELSE
(SELECT * FROM `teacher` order by your_column limit 1)
END
you should also add proper order by on the column your need (in the sample named your_column ) for obtain the valid first row
You can select from both tables using UNION ALL and excluding conditions.
SELECT * FROM `student`
WHERE EXISTS (SELECT * FROM `student` WHERE `join_date` > '2017-03-21 00:00:00')
UNION ALL
SELECT * FROM `teacher`
WHERE NOT EXISTS (SELECT * FROM `student` WHERE `join_date` > '2017-03-21 00:00:00')
Note that the table schemas should be the same.

How many different ways are there to get the second row in a SQL search?

Let's say I was looking for the second most highest record.
Sample Table:
CREATE TABLE `my_table` (
`id` int(2) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`value` int(10),
PRIMARY KEY (`id`)
);
INSERT INTO `my_table` (`id`, `name`, `value`) VALUES (NULL, 'foo', '200'), (NULL, 'bar', '100'), (NULL, 'baz', '0'), (NULL, 'quux', '300');
The second highest value is foo. How many ways can you get this result?
The obvious example is:
SELECT name FROM my_table ORDER BY value DESC LIMIT 1 OFFSET 1;
Can you think of other examples?
I was trying this one, but LIMIT & IN/ALL/ANY/SOME subquery is not supported.
SELECT name FROM my_table WHERE value IN (
SELECT MIN(value) FROM my_table ORDER BY value DESC LIMIT 1
) LIMIT 1;
Eduardo's solution in standard SQL
select *
from (
select id,
name,
value,
row_number() over (order by value) as rn
from my_table t
) t
where rn = 1 -- can pick any row using this
This works on any modern DBMS except MySQL. This solution is usually faster than solutions using sub-selects. It also can easily return the 2nd, 3rd, ... row (again this is achievable with Eduardo's solution as well).
It can also be adjusted to count by groups (adding a partition by) so the "greatest-n-per-group" problem can be solved with the same pattern.
Here is a SQLFiddle to play around with: http://sqlfiddle.com/#!12/286d0/1
This only works for exactly the second highest:
SELECT * FROM my_table two
WHERE EXISTS (
SELECT * FROM my_table one
WHERE one.value > two.value
AND NOT EXISTS (
SELECT * FROM my_table zero
WHERE zero.value > one.value
)
)
LIMIT 1
;
This one emulates a window function rank() for platforms that don't have them. It can also be adapted for ranks <> 2 by altering one constant:
SELECT one.*
-- , 1+COALESCE(agg.rnk,0) AS rnk
FROM my_table one
LEFT JOIN (
SELECT one.id , COUNT(*) AS rnk
FROM my_table one
JOIN my_table cnt ON cnt.value > one.value
GROUP BY one.id
) agg ON agg.id = one.id
WHERE agg.rnk=1 -- the aggregate starts counting at zero
;
Both solutions need functional self-joins (I don't know if mysql allows them, IIRC it only disallows them if the table is the target for updates or deletes)
The below one does not need window functions, but uses a recursive query to enumerate the rankings:
WITH RECURSIVE agg AS (
SELECT one.id
, one.value
, 1 AS rnk
FROM my_table one
WHERE NOT EXISTS (
SELECT * FROM my_table zero
WHERE zero.value > one.value
)
UNION ALL
SELECT two.id
, two.value
, agg.rnk+1 AS rnk
FROM my_table two
JOIN agg ON two.value < agg.value
WHERE NOT EXISTS (
SELECT * FROM my_table nx
WHERE nx.value > two.value
AND nx.value < agg.value
)
)
SELECT * FROM agg
WHERE rnk = 2
;
(the recursive query will not work in mysql, obviously)
You can use inline initialization like this:
select * from (
select id,
name,
value,
#curRank := #curRank + 1 AS rank
from my_table t, (SELECT #curRank := 0) r
order by value desc
) tb
where tb.rank = 2
SELECT name
FROM my_table
WHERE value < (SELECT max(value) FROM my_table)
ORDER BY value DESC
LIMIT 1
SELECT name
FROM my_table
WHERE value = (
SELECT min(r.value)
FROM (
SELECT name, value
FROM my_table
ORDER BY value DESC
LIMIT 2
) r
)
LIMIT 1

How can I refer to a TEMPORARY table more than once in the same query?

the MySQL docs say: "You cannot refer to a TEMPORARY table more than once in the same query."
I know this has been asked before. But I can't find a specific solution for the following.
I'm doing a preselection into a temporary table
CREATE TEMPORARY TABLE preselection AS SELECT ...;
now I wanna do some (around 20 or even 30) unions
(SELECT FROM preselection ...)
UNION
(SELECT FROM preselection ...)
UNION
......
UNION
(SELECT FROM preselection ...)
I could make 20 or 30 copies of preselection and do each select on each table but if I understand it right this is the same as invoke the preselection-query above in every SELECT inside the UNION chain as a subquery.
Is there a way to work around this issue?
Greetings,
chris
Full query:
CREATE TEMPORARY TABLE preselection AS
(
SELECT id, title, chapter, date2, date, snid, max(score) FROM `movies`
WHERE
(
cluster is not NULL
)
AND
(
`date` <= '2012-02-20 05:20:00'
AND `date` > '2012-02-19 17:20:00'
AND (TIMEDIFF(date, date2) < '12:00:00')
)
GROUP BY cluster
)
UNION
(
SELECT id, title, chapter, date2, date, snid, score FROM `movies`
WHERE cluster IS NULL
AND
(
`date` <= '2012-02-20 05:20:00' AND `date` > '2012-02-19 17:20:00' AND (TIMEDIFF(date, date2) < '12:00:00')
)
);
(SELECT * FROM preselection WHERE snid=1 AND chapter LIKE '#A_OT%'
DESC LIMIT 4)
UNION
…
UNION
(SELECT * FROM preselection WHERE snid=19 AND chapter LIKE '#A_OT%'
LIMIT 4)
UNION
... for each chapter from A to J and every snid from 1 to 19 ...
UNION
(SELECT * FROM preselection WHERE snid=1 AND chapter LIKE '#J_OT%'
LIMIT 4)
UNION
…
UNION
(SELECT * FROM preselection WHERE snid=19 AND chapter LIKE '#J_OT%'
LIMIT 4)
ORDER BY `score` DESC, `date`;
I think the error message is clear: you can't do that with a single temporary table. Does creating a view of the data, instead of a temporary tables, do the trick?
Views in mysql
Yes it can be frustrating. The solutions I typically use involve avoiding the temporary table with a more complex query or using more temporary tables.
A simple copy (but like you mentioned this is no use to you)
CREATE TEMPORARY TABLE preselectionCopy AS
SELECT * FROM preselection;
Or a temporary table to hold your results
CREATE TEMPORARY TABLE result (
id INT NULL,
title VARCHAR(256) NULL
...
);
...which can be as simple as
CREATE TEMPORARY TABLE result AS
SELECT * FROM preselection WHERE snid=1 AND chapter LIKE '#A_OT%'
LIMIT 4;
INSERT INTO result
SELECT * FROM preselection WHERE snid=19 AND chapter LIKE '#A_OT%'
LIMIT 4;
...
SELECT * FROM result
ORDER BY `score` DESC, `date`;

Every derived table must have its own alias - error from combination descending MySQL

I want to order one mysql table by two strtotime timestamps from two different columns. I've got the following mysql command:
SELECT * FROM (
(SELECT '1' AS `table`, `vid_req_timestamp` AS `timestamp`, `title` FROM `movies` WHERE `vid_req` = '1')
UNION
(SELECT '2' AS `table`, `ost_req_timestamp` AS `timestamp`, `title` FROM `movies` WHERE `ost_req` = '1')
)
ORDER BY `timestamp` DESC
This gives me an error:
#1248 - Every derived table must have its own alias
I want to combine vid_req_timestamp and ost_req_timestamp and make those descending. And it's important to know where the timestamp came from (somehow).
In this case, the derived table that requires an alias is the one that you are SELECTing * from.
Indentation helps make that clearer.
SELECT * FROM
(
(SELECT '1' AS `table`, `vid_req_timestamp` AS `timestamp`, `title` FROM `movies` WHERE `vid_req` = '1')
UNION
(SELECT '2' AS `table`, `ost_req_timestamp` AS `timestamp`, `title` FROM `movies` WHERE `ost_req` = '1')
) AS `some_table_name_lol_this_is_an_alias`
ORDER BY `timestamp` DESC