Select Multiple Values From Single Column - mysql

I would like to select multiple values from a single column in a database table that equal to a number of values. I want all these values to match otherwise it should return no rows. I do not want to use "IN" as that is equal to "OR".
The following is a basic mockup of what it should do but it needs to be dynamic as I wish to use it with a PDO statement. If the database only contains id's 1 and 2 it should fail ie return no rows.
SELECT
id
FROM
reports
WHERE
id=1 AND id=2 AND id=3
I have the current code as follow which is incorrectly returning zero rows:
SELECT id,title
FROM reports
WHERE id IN (1,2)
GROUP BY title
HAVING COUNT(DISTINCT id) = 2
My current table structure is as follows:
http://www.sqlfiddle.com/#!2/ce4aa/1

You have to use HAVING COUNT(id) = 3 to ensure that the selected rows have all the three id's. Something like:
SELECT *
FROM reports
WHERE id = 1 OR id = 2 OR id = 3 -- Or id IN(1, 2, 3)
GROUP BY SomeOtherField
HAVING COUNT(DISTINCT id) = 3;
Or:
SELECT *
FROM reports
WHERE SomeOtherField IN (SELECT SomeOtherField
FROM reports
WHERE id = 1 or id = 2 -- Or id IN(1, 2, 3)
GROUP BY SomeOtherField
HAVING COUNT(DISTINCT id) = 3
);
Note that: You have to GROUP BY SomeOtherField where SomeOtherField is other field than id because if you GROUP BY id with HAVING COUNT(id) you won't get any records, since COUNT(id) will be always = 1.
Edit: fixed WHERE clause, OR's instead of AND's.
SQL Fiddle Demo

Related

Selecting row value only if other rows are equal

I have a table with an id column and a source column.
I want to return only the source values that all ids share.
E.g. in the table below id 1,2,3 all share 10 and 20, but id 3 is missing the source value 30, so 30 is not valid and I want to return 10 and 20.
I'm using MySQL and want to put this in a stored procedure.
How do I do this?
id
source
1
10
1
20
1
30
2
10
2
20
2
30
3
10
3
20
You may use COUNT(DISTINCT) function as the following:
SELECT source FROM
table_name
GROUP BY source
HAVING COUNT(DISTINCT id)=(SELECT COUNT(DISTINCT id) FROM table_name)
To do this within a stored procedure:
CREATE PROCEDURE getSourceWithAllIds()
BEGIN
SELECT source FROM
table_name
GROUP BY source
HAVING COUNT(DISTINCT id)=(SELECT COUNT(DISTINCT id) FROM table_name);
END
The idea is to select the count of distinct id values for each source, which is done by COUNT(DISTINCT id)... GROUP BY source, then match this count with the distinct count of all id values existed in the table; HAVING COUNT(DISTINCT id)=(SELECT COUNT(DISTINCT id) FROM table_name).
If the two counts are equal, then the source have all the distinct ids existed in the table.
i.e. All distinct ids in the table are (1, 2, 3) count = 3, and distinct ids for a source =10 are (1, 2, 3) count=3. For source = 30, the distinct ids are (1, 2) count=2 so it will not be returned by the query (2<>3).
See a demo.

Find duplicates where entries within id are duplicate

id class count day
1 2 5 5
2 2 4 5
3 2 4 5
3 2 4 5
4 2 5 3
4 1 5 3
4 2 5 3
So I have a query for finding all duplicates based on multiple columns, however, this also returns id's where not all entries are duplicate.
In above example, the query should only show/count id 3, as all the entries with id 3 are duplicate. Even though id 4 also has a duplicate, it should not show as it has another entry that is unique.
Any idea how to do this?
If you need rows with id where there is no row with the same id and unique row values then use NOT IN and HAVING
select *
from your_table t1
where t1.id not in(
select id
from your_table
group by id, class, count, day
having count(*) = 1
)
You can use this query : http://sqlfiddle.com/#!9/1a2536/8
select id
from test
group by id
having count(distinct id,class,count,day) = 1 and count(*)>1
you group each rows by id and count how many different row the group has, if the distinct total is 1 and the total row is > 1 , there is only duplicate rows for this id.
It's quite easy, a quick note it's a very bad idea to name a column count :
SELECT id, class, `count`,day, COUNT(*)
FROM myTable
GROUP BY id, class, `count`,day
HAVING COUNT(*) > 1
edit : I misread the question so here is my solution :
SELECT test.id, test.class, test.count, test.day , count(*), t.countID
FROM (SELECT id, Count(id) AS countID FROM test GROUP BY id ) t
INNER JOIN test on t.id = test.id
GROUP BY test.id, test.class, test.count, test.day
HAVING Count(*) > 1 AND t.countID = count(*)
I came up with this :
SELECT *
FROM (SELECT id,
Count(id) AS matched
FROM test
GROUP BY id,
class,
count,
day) t
GROUP BY id , matched
HAVING Count(id) = 1
AND matched >= 2
There is maybe a more efficient way to do it but it is easier to understand this way, first we group by every column to find duplicate data. Then the first part of the having eliminates the entries that actually have different variants by id and then we keep only the lines that actually only have duplicates.
Edit : compatible with "only_full_group_by" mode

MYSQL Select with IF in where clause

I have a list of items in MySQL table each one has a group number & unique item number.
I'm Trying to select records that mach 2 criterias.
First I need to select record that satisfies group & item numbers. If none found i want records selected that just belongs to a group.
Only one record has to be selected either way.
SELECT *
FROM YourTable
WHERE GroupID = :group
ORDER BY ItemID = :item DESC
LIMIT 1
LIMIT 1 makes it return just one record. The ORDER BY clause makes it prefer a record that matches the item criteria if there is one.
Try this
this won't work
Select * From MyTable
Where (GroupID = 1 and ItemID = 2) or (GroupID = 1)
this works
IF EXISTS (SELECT 1 FROM MyTable WHERE GroupID = 1 AND ItemID = 2)
BEGIN
SELECT TOP 1 * FROM MyTable WHERE GroupID = 1 AND ItemID = 2
END
ELSE
BEGIN
SELECT TOP 1 * FROM MyTable WHERE GroupID = 1
END
replace MyTable with the name of your table
replace GroupID and ItemID with the name of your columns
replace number 1 and 2 with whichever value associate with columns you wish to filter

Get count of ID for name-value pair when name doesn't exist

In mysql - table like this:
ID Name Value
1 Color Blue
1 Weight 50
1 Type Fruit
2 Color Red
2 Weight 40
3 Color Yellow
I want to get a count of distinct ID's that have a name/characteristic of 'Type' and a distinct count of ID's that don't. The first one (those that do) is easy since it's defined, but if I do a select count(distinct ID) where name <> 'type' - ID 1 will still be part of that count as it has other rows/attributes that <> 'type'.
In this example - the desired result for distinct count = 'type' would be 1 (ID 1) and for distinct count <> 'type' would be 2 (ID's 2 & 3).
Thanks in advance.
This is similar to SQL Query User has one record in the table but not another one
You can select where the id is not in a subquery looking for ids that have type
select count(id) from table where id not in (select id from table where name = 'type')
group by id
for this particular task you can use:
select count(distinct ID) from table
where ID in (select ID from table where name='type') --- this will give you count of IDs where type exists
select count(distinct ID) from table
where ID not in (select ID from table where name='type') -- this will give you count of IDs without type
SELECT id FROM test GROUP BY id HAVING
CONCAT(",",CONCAT(GROUP_CONCAT(name), ","))
NOT LIKE '%,Type,%'
will give you all ids without Type: http://sqlfiddle.com/#!2/30837/1
(Concat with , ensures, that you are not matching XType by accident.)
while
SELECT COUNT(id) AS count, GROUP_CONCAT(id) AS ids
FROM(SELECT id, count(name) as count FROM test
GROUP BY id HAVING CONCAT(",",CONCAT(GROUP_CONCAT(name), ","))
NOT LIKE '%,Type,%') as temp;
will give you the desired count: http://sqlfiddle.com/#!2/30837/9
SELECT CASE
WHEN Name='Type' THEN 'Type'
ELSE 'Non-Type'
END Name
,ID
,COUNT(ID)
FROM Stuff
GROUP BY
CASE
WHEN Name='Type' THEN 'Type'
ELSE 'Non-Type'
END
,ID
See SQLFiddle

mysql select rows with same ids and preserve their order?

just a quick question:
i have to have one single query that has multiple rows - some rows are identicle - and the order of rows must be preserved in the result -
some idea of what im refering to:
SELECT id,date
FROM items
WHERE id IN (1,2,1,3)
ORDER BY id=1 DESC,id=2 DESC,id=1 DESC,id=3 DESC;
unfortunately mysql result is this:
1,2,3
not 1,2,1,3
it removes the duplicate which i have to have in my result to display in multiple panels on the same webpage -
i really dont want to loop thru each id one by one to get them the way i want to display -
is there a way to actually have one single query that will preserve the order and pull out rows based on request whether its unique or not -
Your query as it stands will never work, because duplicate values in a list of values of an IN clause are ignored. The only way to make this work is by using UNION ALL:
SELECT id, date FROM items where id = 1
UNION ALL
SELECT id, date FROM items where id = 2
UNION ALL
SELECT id, date FROM items where id = 1
UNION ALL
SELECT id, date FROM items where id = 3;
But to be frank, I suspect your data model so far past screwed it's unusable.
try
SELECT
id,
date
FROM items
WHERE id IN (1,2,1,3)
ORDER BY FIND_IN_SET(id, '1,2,1,3')
Another scrupulous way to answer a suspicious question:
SELECT
items.id,
items.date
FROM
items
JOIN
( SELECT 1 AS id, 1 AS ordering
UNION ALL
SELECT 2, 2
UNION ALL
SELECT 1, 3
UNION ALL
SELECT 3, 4
) AS auxilary
ON
auxilary.id = items.id
ORDER BY
auxilary.ordering
Another approach (untested, but should give you the idea):
CREATE TEMPORARY TABLE tt (id INT, ai int unsigned auto_increment primary key);
INSERT INTO tt (id) VALUES (1), (2), (1), (3);
SELECT
id,
date
FROM items JOIN tt USING (id)
ORDER BY tt.ai;
keeps the given order.
If you want to include the records with id=1 and the order doesn't matter as long as you get them, you can split your query into two queries, one for (1,2,3) union all the other query for id=1 or just do:
... In (1,2)
Union all
... In (1,3)
Example:
Select * from
(Select case id when 1 then 1 when 2 then 2 as pseudocol, othercolumns
From table where Id in (1,2)
Union all
Select case id when 1 then 3 when 3 then 4 as pseudocol, othercolumns
From table where Id in (1,3)) t order by pseudocol
Instead of doing what you are trying to, just select the unique rows you need. In the frontend code, store each unique row once in a key=>value structure, where key is the item ID and value is whatever data you need about that item.
Once you have that you can use frontend logic to output them in the desired order including duplicates. This will reduce the amount of redundant data you are trying to select.
For example This is not usable code - exact syntax required depends on your scripting language
-- setup a display order
displayOrder= [1,2,1,3];
-- select data from database, order doesn't matter here
SELECT id,date
FROM items
WHERE id IN (displayOrder);
-- cache the results in a key=> value array
arrCachedRows = {};
for (.... each db row returned ...) {
arrCachedRows[id] = date;
}
-- Now output in desired order
for (listIndex in displayOrder) {
-- Make sure the index is cached
if (listIndex exists in arrCachedRow) {
echo arrCachedRows[listIndex ];
}
}
If you must persist in using UNION despite my warnings
If you go against the above recommendation and absolutely MUST have them back in 1 query in that order then add on an additional row which will enforce the row order. See below query where I use variable #subIndex to add an incrementing value as subIndex. This in turn lets you reorder by that and it'll be in the requested order.
SELECT
i.*
FROM (
SELECT #subIndex:=#subIndex+1 AS subIndex, id, date FROM items where id = 1
UNION
SELECT #subIndex:=#subIndex+1 AS subIndex, id, date FROM items where id = 2
UNION
SELECT #subIndex:=#subIndex+1 AS subIndex, id, date FROM items where id = 1
UNION
SELECT #subIndex:=#subIndex+1 AS subIndex, id, date FROM items where id = 3
) AS i,(SELECT #subIndex:=0) v
ORDER BY i.subIndex
Or a slightly cleaner version that keeps item selection until the outside and hides the subindex
SELECT
items.*
FROM items
-- initialise variable
INNER JOIN (SELECT #subIndex:=0) v
-- create a meta-table with the ids desired in the order desired
INNER JOIN (
SELECT #subIndex:=#subIndex+1 AS subIndex, 1 AS id
UNION
SELECT #subIndex:=#subIndex+1 AS subIndex, 2 AS id
UNION
SELECT #subIndex:=#subIndex+1 AS subIndex, 1 AS id
UNION
SELECT #subIndex:=#subIndex+1 AS subIndex, 3 AS id
) AS i
ON i.id = items.id
-- order by the subindex from i
ORDER BY i.`subIndex` ASC