I had lots of tables and had performed multiple join lefts all the tables without incorrect or duplicate results. One of my table is multi-values, can accept duplicate same ids. When I left joins all the tables, the result shows correct, but that particular column show one of the values from multi-values table.
Currently I'm doing filtering the multi-values table, but the result is unforseen because it only output single one of the value from multi-values table.
Menu Table
id cuisine_id
1 1
CuisineMenu Table (Multi-values / Accept duplicated menu's id)
cuisine_id menu_id
1 1
2 2 (Ignore)
3 1
Query
SELECT * FROM menu
LEFT JOIN (
SELECT *
FROM cuisinemenu
GROUP BY menu_id
) cuisinemenu ON cuisinemenu.menu_id = menu.id
WHERE ( cuisine_id = '3')
Output
Without Where Clause
id cuisine_id
1 1
With Where Clause
Empty
Expected Result (With Where Clause)
id cuisine_id
1 3
You can use simple LEFT JOIN, try below:
SELECT m.*
FROM Menu m LEFT JOIN CuisineMenu cm ON m.id = cm.menu.id
WHERE cm.cuisine_id = 3;
This should result in 1 row.
Change your sql code from following:
SELECT *
FROM cuisinemenu
GROUP BY menu_id
to this:
SELECT *
FROM cuisinemenu
GROUP BY cuisine_id
the first one shows only menu_id without showing the cuisine_id.
Related
I have a table which has certain IDs, and values against them. If I pass a list of IDs in the where clause it will return only matching IDs, and discard the rest. How can I get the unmatched IDs also with a null value against them.
Table:
select * from members;
Result:
|member_id |price |
+--------------+-------
| 0 |1234 |
| 1 |99 |
+--------------+-------
However if we run this query, it won't return the unmatched values.
select member_id,price from members where member_id in (0,1,2,3)
How can I show IDs 2&3 as well, but with a null against them for price column?
Thanks!
The values can't appear exclusively in the where clause; they must appear somehow in the from clause. You must create a view of some sort including those values. A common way is to use an Oracle-provided collection type and select from it, something like this:
select t.column_value as member_id, m.price
from sys.odcinumberlist(0, 1, 2, 3) t left outer join members m
on t.column_value = m.member_id
;
MEMBER_ID PRICE
---------- ----------
0 1234
1 99
2
3
odcinumberlist is a collection data type (varray of number) defined in the sys package, provided by Oracle. When used in the from clause of a select statement, the values are in a column named column_value.
Whether this will work in "mariadb" (whatever that is), you will have to try and see. If it does not, you can try something more traditional, like
select t.member_id, m.price
from (
select 0 as member_id from dual union all
select 1 from dual union all
select 2 from dual union all
select 3 from dual
) t
left outer join members m on t.member_id = m.member_id
;
You can use a LEFT JOIN for this:
SELECT m.member_id, m.price
FROM members m
LEFT JOIN members m2 ON m2.member_id = m.member_id
AND m2.member_id IN (0,1,2,3)
WHERE m2.member_id IS NULL
I need to make one SQL command.
From table with comments i'll get comment id, then
with this ID I need to get count of reactions with the same comment ID and user's names.
So for example I have this 2 tables:
Comments:
ID
Comm_text
1
Example text
2
Another example
and Reactions:
ID
comm_id
usr
etc..
1
1
Peter
another
2
1
John
collon
3
1
Dog
cuz
4
2
Cat
why not
I need to get this:
ID
Comm_text
Reactions_Count
Users
1
Example text
3
Peter, John, Dog
2
Another example
1
Cat
I tried this:
SELECT k.id, k.comm, COUNT(r.id) as reactions, r.usr
FROM `comms` k
INNER JOIN `reactions` r ON r.id=k.id
It's just one row with one comment and count of all rows in reaction table.
Thanks.
Try this query that makes the same output:
select comments.id as ID , comments.Comm_text as Comm_text ,
(select count(id) from Reactions where comm_id = comments.id) as Reactions_Count ,
(select coalesce(GROUP_CONCAT(usr ORDER BY usr DESC) , '') from Reactions WHERE comm_id = comments.id) as Users
from comments group by comments.id
You should use group by to group the comments and have just one row then use query to count and gather the data, based on each row of the group.
The GROUP_CONCAT attach the output with , and the coalesce set the output to a given string if the output was empty.
Read more about:
GROUP BY
GROUP_CONCAT
COALESCE
subquery
According to the names that u set in the example, this will work. Just fix the table names for your database structure.
SELECT `Comments`.`ID`, `Comments`.`Comm_text`, count(`Reactions`.`comm_id`) as react, `Reactions`.`usr`
FROM `Comments`
INNER JOIN `Reactions`
ON `Comments`.`ID`=`Reactions`.`comm_id`
GROUP BY `Reactions`.`comm_id`
I have 3 tables: tags, products and relation table between them.
Relation table looks for example like this:
tagId | ProductId
1 | 1
2 | 1
2 | 9
The user can pick two options "All of these" or "One of these".
So if user picks All of these, it's means that the product must have exactly all of tags which the user chose.
So if user pick tags with id 1 and 2, it should select only product with id 1, because this product has exactly the same tags the user chose. (Another way is if the user picks the tag with id 2, it should select only product with id 9.)
So, the product has to have all tags which the user chose (no more, no less).
SQL that I already have for Any/One of these:
SELECT DISTINCT s.SKU
FROM SKUToEAN as s
LEFT JOIN ProductDetails as p ON s.ProductDetailID=p.id
JOIN ProductTagRelation as ptr ON (ptr.productId=p.id and ptr.tagId IN(Ids of selected tags))
Example behavior:
TagId = 1 it should select => None
TagId = 2 it should select => 9
TagId = 1,2 it should select = 1,9
So probably I need two queries. One for any/one of these ( I already have this one ) and the second for all of these.
With PHP I decide which query to use.
You can GROUP BY on the ProductID and use conditional aggregation based filtering inside the Having clause. MySQL automatically casts boolean values to 0/1 when using in numeric context. So, in order to have a specific tagID value available against a ProductID, its SUM(tagId = ..) should be 1.
All of these:
SELECT ptr.productId, s.SKU
FROM SKUToEAN AS s
LEFT JOIN ProductDetails AS p
ON p.id = s.ProductDetailID
JOIN ProductTagRelation AS ptr
ON ptr.productId = p.id
GROUP BY ptr.productId, s.SKU
HAVING SUM(ptr.tagID = 1) AND -- 1 should be there
SUM(ptr.tagID = 2) AND -- 2 should be there
NOT SUM(ptr.tagID NOT IN (1,2)) -- other than 1,2 should not be there
Is this you are looking for (for all condition)?
select product.id
from products
inner join <table> on products.id = <table>.productId
group by product.id
having group_concat(<table>.tagId order by <table>.tagId separator ',') = '1,2';
i've tried both of these and continue to get a duplicate for each credit data entry:
SELECT DISTINCT * FROM
FROM `mediaDATA`
LEFT JOIN media_creditsDATA ON mediaDATA.id = media_creditsDATA.media_id
SELECT *
FROM `mediaDATA`
LEFT JOIN media_creditsDATA ON mediaDATA.id = media_creditsDATA.media_id
First, use distinct * is counterintuitive, you are essentially selecting every row in the table then eliminating duplicate rows. Try to avoid using that.
since you have tried distinct it eliminated the possibility where you start off with duplicate data in your tables.
looking at your screenshot I think the rows are not duplicate. They might be identical on certain columns but can't be completely identical. for example.
media:
id name
----------- ---------------
1 mediaA
2 mediaB
3 mediaC
media_creditsDATA:
media_id credit_id name
----------- ----------- ---------------
1 1 good credit
1 2 ok credit
2 3 bad credit
3 4 no credit
if you execute the following sql with distinct or not the result is the same:
SELECT *
FROM media
INNER JOIN media_creditsDATA ON media.id = media_creditsDATA.media_id
result:
id name media_id credit_id name
----------- --------------- ----------- ----------- ---------------
1 mediaA 1 1 good credit
1 mediaA 1 2 ok credit
2 mediaB 2 3 bad credit
3 mediaC 3 4 no credit
If you only look at the first three columns in the result table then sure there are duplicate records, but not if you look at all the columns. As you can see the media table has a one to many relationship to media_creditsDATA table. The result table has records that share the same subset of columns but there are no duplicate records.
so I think the problem in this case is not how you join is how you filter your result. such as is there a subset of credit records you are looking for in media_creditsDATA table? or maybe you don't care and you just record with highest credit_id for each media records.
SELECT *
FROM media
INNER JOIN (
select media_id, max(credit_id) as highest_credit_id from media_creditsDATA
group by media_id )media_creditsDATA ON media.id = media_creditsDATA.media_id
you get:
id name media_id highest_credit_id
----------- --------------- ----------- --------------
1 mediaA 1 2
2 mediaB 2 3
3 mediaC 3 4
if you don't want duplicated row you should use distinct and explicit column name for only the values you really need
eg:
SELECT distinct id, company_id, associated_company_id, title, year, `desc`, media_file
FROM mediaDATA
LEFT JOIN media_creditsDATA ON mediaDATA.id = media_creditsDATA.media_id
select * from mediaDATA LEFT JOIN media_creditsDATA
ON mediaDATA.id = media_creditsDATA.media_id
where mediaDATA.id in (
select DISTINCT(media_id)
from media_creditsDATA
)
most people would say that you should add a DISTINCT on the id on the main Select. You can try that, but Im quite sure it will decrese performance.
You are getting duplicates in your first statement because there are multiple rows in the media_creditsDATA table with the same media_id, as it is not the PK of media_creditsData. The non-key data of this table could be the same across all media_ids (which would be a strange model), but we certainly cannot assume so. Given that, distinct will be unreliable for this purpose.
You have a couple of options:
Select a distinct subset of fields from media_creditsDATA and join to it, like:
select *
from mediaData
LEFT OUTER JOIN
(select distinct media_id, field1, field2, field3 from media_creditsDATA) t
ON t.media_id = mediaData.id
This will work so long as there are no variations among records with the same media_id for the fields needed. If there are variations, you will again see duplicates.
A more reliable option, would be to decide what aggregation criteria makes sense for the media_creditsDATA records. If you don't want more than one record when more than one exists, which do you want? Perhaps there is a way to find the latest, first, etc.? This query could look something like this:
Select *
from mediaData
LEFT OUTER JOIN
(select *
from media_creditsDATA
inner join
(select media_id, max(%some_date% or %some_id%
from media_creditsDATA
group by media_id) mc_t
on mc_t.media_id = media_creditsDATA.media_id
and mc_t.%aggregated_column% = media_creditsDATA.%same_column) t
ON t.media_id = mediaData.id
This would ensure that the subquery will return exactly 1 or 0 rows.
It seems I don't get it something. Please consider this query
SELECT COUNT(*) AS `numrows`
FROM (`exp_channel_titles` ch)
JOIN `exp_channel_data` cd ON `cd`.`entry_id`=`ch`.`entry_id`
LEFT JOIN `exp_matrix_data` md ON `md`.`entry_id`=`ch`.`entry_id` and field_id = 14
LEFT JOIN `exp_assessment_users` au ON `au`.`entry_id`=`ch`.`entry_id`
WHERE ch.channel_id = 4 GROUP BY `ch`.`entry_id`
it returns 2
but if I change it to
SELECT *
FROM (`exp_channel_titles` ch)
JOIN `exp_channel_data` cd ON `cd`.`entry_id`=`ch`.`entry_id`
LEFT JOIN `exp_matrix_data` md ON `md`.`entry_id`=`ch`.`entry_id` and field_id = 14
LEFT JOIN `exp_assessment_users` au ON `au`.`entry_id`=`ch`.`entry_id`
WHERE ch.channel_id = 4 GROUP BY `ch`.`entry_id`
result is 1 row only. How so?
You're grouping, which means internally matching rows are collapsed into a single entity. e.g. consider a fake table like this:
field
-----
a
a
Yes, a one field table, with two records, both of which have the value a in them.
SELECT *
FROM table
GROUP BY field
group by will find all fields which have the same value, and collapse them down into a SINGLE record, so your two records of a become one row in the result set, and you end up with
field
-----
a
But doing
SELECT count(*)
FROM table
GROUP BY field
changes things. Now the DB will literally count how many records were collapsed down into the single row of result set. So you still get a SINGLE row in the result set, which contains a count of how many rows were collapsed by the group by:
count(*)
--------
2
One row, with a value of 2, because there were two rows with a.
Now if you had a table with more records:
field
-----
a
a
b
c
c
c
You would get:
SELECT * ... GROUP BY field
field
-----
a
b
c
SELECT count(*), field ... GROUP BY field
count(*) field
----------------
2 a
1 b
3 c
again, 3 rows of results, but note how the count represents how many of each grouped field there are in the original table.