MySQL - Add values to the IN clause? - mysql

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

Related

Check which IDs from a set do not exists in a table

I have a list of user IDs, like this:
757392,733602,749955,744304,746852,753904,755117,636163,564931,740787,751450,743799,643918,
749903,571888,30207,705953,749120,749001,749192,749978,750840,544228,702121,746246,383667,
558790,585628,592771,745818,749375,241209,749235,746860,748318,748016,748951,747321,748684,
748225,565375,748673,747869,748522,748335,744775,672229,578056,713127,740234,632608,711135,
746528,362131,742223,746567,745224,332989,439837,745418,673582,269584,742606,745135,746950,
476134,740830,742949,276934
I have a MySQL table users with the id field.
How do I check - using a query - which IDs of the ones I have do not exists in the users table?
This sounds like a simple problem to me, yet I couldn't find any example on StackOverflow which would address a fixed set of ID values.
I didn't know the find_in_set() function and took a more handcrafted approach. Not that it makes any sense given the first answer, but I'll post it anyway:
SELECT id
FROM (
SELECT '757392' AS id UNION
SELECT '733602' UNION
SELECT '749955' UNION
SELECT '744304' UNION
SELECT '746852' UNION
SELECT '753904' UNION
SELECT '755117' UNION
SELECT '636163' UNION
SELECT '564931' UNION
SELECT '740787' UNION
SELECT '751450' UNION
SELECT '743799' UNION
SELECT '643918' UNION
SELECT '749903' UNION
SELECT '571888' UNION
SELECT '30207' UNION
SELECT '705953' UNION
SELECT '749120' UNION
SELECT '749001' UNION
SELECT '749192' UNION
SELECT '749978' UNION
SELECT '750840' UNION
SELECT '544228' UNION
SELECT '702121' UNION
SELECT '746246' UNION
SELECT '383667' UNION
SELECT '558790' UNION
SELECT '585628' UNION
SELECT '592771' UNION
SELECT '745818' UNION
SELECT '749375' UNION
SELECT '241209' UNION
SELECT '749235' UNION
SELECT '746860' UNION
SELECT '748318' UNION
SELECT '748016' UNION
SELECT '748951' UNION
SELECT '747321' UNION
SELECT '748684' UNION
SELECT '748225' UNION
SELECT '565375' UNION
SELECT '748673' UNION
SELECT '747869' UNION
SELECT '748522' UNION
SELECT '748335' UNION
SELECT '744775' UNION
SELECT '672229' UNION
SELECT '578056' UNION
SELECT '713127' UNION
SELECT '740234' UNION
SELECT '632608' UNION
SELECT '711135' UNION
SELECT '746528' UNION
SELECT '362131' UNION
SELECT '742223' UNION
SELECT '746567' UNION
SELECT '745224' UNION
SELECT '332989' UNION
SELECT '439837' UNION
SELECT '745418' UNION
SELECT '673582' UNION
SELECT '269584' UNION
SELECT '742606' UNION
SELECT '745135' UNION
SELECT '746950' UNION
SELECT '476134' UNION
SELECT '740830' UNION
SELECT '742949' UNION
SELECT '276934') AS id_list
WHERE id NOT IN (
SELECT id
FROM users);
This is an option:
SELECT ids.id
FROM ( SELECT #i
, substring(#string, #start, #end-#start) id
FROM <BigTable>
, ( SELECT #string := <YourStringOfIds>
, #start:=0
, #end:=0
, #i:=0
, #len:=length(#string)
, #n:=#len-length(replace(#string,',',''))+1
) t
WHERE (#i := #i+1) <= #n
AND (#start := #end+1)
AND (#loc := locate(',',#string,#start))
AND #end := if(#loc!=0,#loc,#len+1)
) ids
LEFT JOIN <BigTable> u
ON u.id = ids.id
WHERE u.id is null
BigTable can be any table whose number of rows >= number of ids in your string.
Create temporary table, then fill it
CREATE TABLE tmp (
`id` INT NOT NULL,
PRIMARY KEY (`id`));
INSERT INTO tmp (id) VALUES (1),(2),(3),(4),(5),(6)
then make a query
SELECT tmp.id
FROM tmp
LEFT JOIN users u ON u.id = tmp.id
WHERE tmp.id IS NULL
finally drop the table
DROP TABLE tmp
You're looking for the IN clause with a negation. I.e. you can specify your list as the argument to the IN clause like so:
SELECT * FROM users
WHERE id NOT IN ( 757392,733602,749955,744304,746852,753904,755117,636163,564931,740787,751450,743799,643918,749903,571888,30207,705953,749120,749001,749192,749978,750840,544228,702121,746246,383667,558790,585628,592771,745818,749375,241209,749235,746860,748318,748016,748951,747321,748684,748225,565375,748673,747869,748522,748335,744775,672229,578056,713127,740234,632608,711135,746528,362131,742223,746567,745224,332989,439837,745418,673582,269584,742606,745135,746950,476134,740830,742949,276934 );
UPDATE
My bad - I didn't read the question properly.
So the correct way would be to go with UNIONs then outer join and filter by NULL, like this:
SELECT WantedIds.id
FROM users
RIGHT JOIN (
SELECT x.id
FROM (
SELECT '757392' AS id UNION
SELECT '733602' UNION
SELECT '749955' UNION
SELECT '744304' UNION
SELECT '746852' UNION
SELECT '753904' UNION
SELECT '755117' UNION
SELECT '636163' UNION
SELECT '564931' UNION
.
.
.
) x
) WantedIds
ON WantedIds.id = users.id
WHERE users.id IS NULL
You can use MySQL's find_in_set() function to check if a value exists in a commase separated list of values:
select * from your_table
where find_in_set(field_name,'757392,733602,749955,744304,746852,753904,755117,636163,564931,740787,751450')=0

Select Count with Distinct and Union All

I have a query like :
SELECT COUNT(DISTINCT `ID`) FROM db1.table UNION ALL SELECT COUNT(DISTINCT `ID`) FROM db2.table
My real query is more complicate, i have LEFT JOIN and multiple condition etc...
This query return to me an array with 2 results : Count 1 and Count 2
How can i only return one result ? Count 1 + Count 2 ?
Thanks !
Try this:
SELECT (SELECT COUNT(DISTINCT `ID`) FROM db1.table) +
(SELECT COUNT(DISTINCT `ID`) FROM db2.table)
If you also want to return separate db1, db2 counts, then use this query:
SELECT countDb1, countDb2, countDb1 + countDb2 AS total
FROM (
SELECT (SELECT COUNT(DISTINCT `ID`) FROM db1.table) AS countDb1,
(SELECT COUNT(DISTINCT `ID`) FROM db2.table) AS countDb2) AS t

Mysql Date Comparison with IN Statement

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;

MySQL use one subquery more times

I'm just beginner in SQL, so please don't worry :)
I have table where I store dictionary word translates. The table have columns id, la (language), lb (target language), wa (original word), wb (translated word) and some more insignificant columns :). I want to show a table overview, where will be a list of all languages with count of words in each language plus row with SUM as a language with total words count (all counts of only distinct words).
I wrote this query:
SELECT `lng`, COUNT(`word`) AS `count`
FROM (
SELECT DISTINCT *
FROM (
SELECT DISTINCT `la` AS `lng`, `wa` AS `word`
FROM `dict_trans`
UNION ALL
SELECT DISTINCT `lb` AS `lng`, `wb` AS `word`
FROM `dict_trans`
) AS `tbla`
) AS `tblb` GROUP BY `lng`
UNION ALL
SELECT 'sum' AS `lng`, COUNT(`word`) AS `count`
FROM (
SELECT DISTINCT *
FROM (
SELECT DISTINCT `la` AS `lng`, `wa` AS `word`
FROM `dict_trans`
UNION ALL
SELECT DISTINCT `lb` AS `lng`, `wb` AS `word`
FROM `dict_trans`
) AS `tblc`
) AS `tbld` ORDER BY `count` DESC
But I think it's very silly and performance unfriendly doing one subquery more times.
SELECT DISTINCT *
FROM (
SELECT DISTINCT `la` AS `lng`, `wa` AS `word`
FROM `dict_trans`
UNION ALL
SELECT DISTINCT `lb` AS `lng`, `wb` AS `word`
FROM `dict_trans`
) AS `tbla`
I tried in second part of code to pass reference to the table from first part:
SELECT `lng`, COUNT(`word`) AS `count`
FROM (
SELECT DISTINCT *
FROM (
SELECT DISTINCT `la` AS `lng`, `wa` AS `word`
FROM `dict_trans`
UNION ALL
SELECT DISTINCT `lb` AS `lng`, `wb` AS `word`
FROM `dict_trans`
) AS `tbla`
) AS `tblb` GROUP BY `lng`
UNION ALL
SELECT 'sum' AS `lng`, COUNT(`word`) AS `count`
FROM `tblb`
ORDER BY `count` DESC
But error was thrown (#1146 - Table 'db.tblb' doesn't exist).
It's possible to solve this problem without of creating temporary tables?
You don't need all the complexity of your query. UNION means UNION DISTINCT so there is no need for the 3-level nesting and the extra count can be done with the WITH ROLLUP modifier:
SELECT lng AS language,
COUNT(word) AS WordCount
FROM
( SELECT la AS lng, wa AS word
FROM dict_trans
UNION -- DISTINCT is the default
SELECT lb, wb
FROM dict_trans
) AS t
GROUP BY lng
WITH ROLLUP ;
And if you want sorting, you'll need another nesting:
SELECT language, WordCount
FROM
( SELECT COALESCE(lng, 'Total') AS language,
COUNT(word) AS WordCount
FROM
( SELECT la AS lng, wa AS word
FROM dict_trans
UNION -- DISTINCT is the default
SELECT lb, wb
FROM dict_trans
) AS t
GROUP BY lng
WITH ROLLUP
) AS tmp
ORDER BY CASE WHEN language = 'Total' THEN 1 ELSE 0 END,
WordCount DESC ;
The most expensive part of this query will be the UNION (because you need DISTINCT) and the GROUP BY operations. If you want efficiency, it would be much better to have a separate table with all distinct language and word combinations and then group by on that table (no union would be needed). And the dict_trans would be a "junction" table and you'd have 2 foreign keys, pointing to that new distinct languages-words table.

MySQL - Want to Minus two SUM() values from two SELECT statements

I want to be able to grab the two values that I have generated in SUM() from two SELECT queries and minus these values in order to get the result (OutstandingFunds).
These are my two SELECT queries:
Statement (1):
SELECT SUM(Cf.Amount) AS ClearedFunds
FROM (
SELECT Amount FROM PAYMENT1 WHERE `Status` = "Cleared"
UNION ALL
SELECT Amount FROM PAYMENT2 WHERE `Status` = "Cleared"
UNION ALL
SELECT Amount FROM PAYMENT3 WHERE `Status` = "Cleared") AS Cf;
Statement (2):
SELECT SUM(Price) AS TotalSales
FROM PROPERTY
WHERE Status = “Sold”;
Thank you for your time
If you don't need to show the separate subtotal for total sales and cleared funds, you can do it like this:
SELECT SUM(Total.`Price`) AS ClearedFunds
FROM (
SELECT `Price` FROM PROPERTY WHERE `Status` = 'Sold'
UNION ALL
SELECT (`Amount` * -1) AS `Price` FROM PAYMENT1 WHERE `Status` = 'Cleared'
UNION ALL
SELECT (`Amount` * -1) AS `Price` FROM PAYMENT2 WHERE `Status` = 'Cleared'
UNION ALL
SELECT (`Amount` * -1) AS `Price` FROM PAYMENT3 WHERE `Status` = 'Cleared'
) AS Total;
I am assuming you are wanting to subtract cleared funds from total sales here.
You were almost there... here's the working SQL:
SELECT (SELECT SUM(Cf.Amount) AS ClearedFunds
FROM (
SELECT Amount FROM PAYMENT1 WHERE `Status` = "Cleared"
UNION ALL
SELECT Amount FROM PAYMENT2 WHERE `Status` = "Cleared"
UNION ALL
SELECT Amount FROM PAYMENT3 WHERE `Status` = "Cleared") as Cf)
- (SELECT SUM(Price) AS TotalSales
FROM PROPERTY
WHERE Status = "Sold") as Result;
Here's the SQL Fiddle so that you can play with test data: http://sqlfiddle.com/#!2/18677/11
You can use SELECT ... INTO ..., in your case:
SELECT SUM(Cf.Amount) AS ClearedFunds
FROM (
SELECT Amount FROM PAYMENT1 WHERE `Status` = "Cleared"
UNION ALL
SELECT Amount FROM PAYMENT2 WHERE `Status` = "Cleared"
UNION ALL
SELECT Amount FROM PAYMENT3 WHERE `Status` = "Cleared") INTO #cf;
SELECT SUM(Price) AS TotalSales
FROM PROPERTY
WHERE Status = “Sold” INTO #ts;
Then you can substract or use them in any other query, like this:
SELECT #ts - #cf;
Are you looking for this?
SELECT C.ClearedFunds - P.TotalSales
FROM (
SELECT SUM(Cf.Amount) AS ClearedFunds
FROM (
SELECT Amount FROM PAYMENT1 WHERE `Status` = "Cleared"
UNION ALL
SELECT Amount FROM PAYMENT2 WHERE `Status` = "Cleared"
UNION ALL
SELECT Amount FROM PAYMENT3 WHERE `Status` = "Cleared") AS Cf) C,
(SELECT SUM(Price) AS TotalSales
FROM PROPERTY
WHERE Status = “Sold”) P
;