x_Id | y_Id | z_Id
----- |----- |-----
1 | 1 | 1
2 | 1 | 1
3 | 1 | 1
4 | 1 | 1
5 | 1 | 1
1 | 2 | 3
I am relatively new at programming and I cant figure out this MySql query. I need to select x_Id only where ((y_Id = 1 AND z_Id = 1) AND (y_Id = 2 AND z_Id = 3)).
Therefore, using these numbers as an example the only thing that should be selected is (x_Id =) 1.
**All of these columns are in the same table
The closest I have come is by using this query:
SELECT
*
FROM
`relationships`
WHERE
y_id = 1 AND
z_id = 1
UNION
SELECT
*
FROM
`relationships`
WHERE
z_id = 3 AND
y_id = 2
However, this returns all the x_ids and x_id = 1 again as a duplicate.
**I am using sqlPro and MySql 5
no need to Union.
Updated after seeing comments:
select
*
from relationships T1
INNER JOIN relationshipsT2 on t1.x_Id = t2.x_Id where
((T1.y_Id = 1 AND T1.z_Id = 1) AND (T2.y_Id = 2 AND T2.zz_Id= 3))
also you can only return x_Id instead of *
If you are only interested in the x_id value you can use the query above, but just add DISTINCT and project only the x_id value.
Example:
SELECT
DISTINCT x_id
FROM
`relationships`
WHERE
y_id = 1 AND z_id = 1
UNION
SELECT
DISTINCT x_id
FROM
`relationships`
WHERE
z_id = 3 AND y_id = 2
There are few other way how to do it, which are even easier such as use OR in the WHERE clause.
Updated after seeing comments:
Using an aggregate SUM() you can total up the number of conditions met per value of x_id. If the total is > 1, both conditions are met somewhere in the table.
SELECT DISTINCT x_id FROM (
SELECT
x_id,
SUMCASE WHEN (y_id = 1 AND z_id = 1) OR (z_id = 3 AND y_id = 2) THEN 1 ELSE 0 END) AS hasboth
FROM relationships
GROUP BY x_id
HAVING hasboth > 1
) subq
Related
I have a database table products with the following columns.
ID | segment_key | segment_value
1 | Mo | 1
2 | Mo | 3
4 | Jo | 1
5 | Jo | 2
6 | Ta | 1
For any given key I need to find the next available segment_value for me to record in the same table.
ie. for the following segment_key list, the expected outputs are
Mo -> 2
Jo -> 3
Ta -> 2
Ji -> 1
I tried the solution mentioned here but I cannot seem to get the right output.
This is my failed attempt.
SELECT t1.segment_value
FROM products t1
WHERE NOT EXISTS (
SELECT *
FROM products t2
WHERE t2.segment_value = t1.segment_value + 1 and t2.segment_key='Mo' and t2.is_active=1
)
LIMIT 1
You can try to use CTE RECURSIVE to get the gap of all values. then do CROSS JOIN fill in the gap of value from each segment_key.
Final using OUTER JOIN and filter segment_key IS NULL which represent the gap of values
Query #1
WITH RECURSIVE CTE AS(
SELECT MIN(segment_value) val,MAX(segment_value) + 1 max_val
FROM products
UNION ALL
SELECT val + 1 ,max_val
FROM CTE c
WHERE val + 1 <= max_val
)
SELECT c.segment_key,MIN(val) segment_value
FROM (
SELECT DISTINCT val,segment_key
FROM CTE
CROSS JOIN products
) c
LEFT JOIN products p
ON c.val = p.segment_value AND c.segment_key = p.segment_key
WHERE p.segment_key IS NULL
GROUP BY c.segment_key;
segment_key
segment_value
Mo
2
Jo
3
Ta
2
View on DB Fiddle
I have this table
uid | rid
-----------
1 | 4
1 | 13
2 | 4
3 | 13
I want my query to return the uid where it has rid = 13 AND does not have rid = 4 so the result would be 3
So far I have
SELECT distinct uid
FROM test
WHERE
ur.rid <> 4
AND ur.rid = 13
but of course this is returning 1 and 3
What am I missing from my query to get only 3?
Many thanks.
You can add NOT EXISTS:
SELECT DISTINCT uid
FROM test t1
WHERE t1.rid = 13
AND NOT EXISTS (SELECT 1
FROM test t2
WHERE t2.uid = t1.uid
AND t2.rid = 4);
LiveDemo
or using aggregation:
SELECT uid
FROM test
GROUP BY uid
HAVING COUNT(CASE WHEN rid = 13 THEN 1 END) > 0
AND COUNT(CASE WHEN rid = 4 THEN 1 END) = 0;
LiveDemo2
EDIT (by gordon):
The above answer is fine. I'm just adding this as a simplification to the second query (it didn't seem appropriate to put in a separate answer and it is too long for a comment):
SELECT uid
FROM test
GROUP BY uid
HAVING SUM(rid = 13) > 0 AND
SUM(rid = 4) = 0;
It takes advantage of a nice feature of MySQL.
SqlFiddleDemo
I'm trying to select the sum of the values in the isOK column for each Name separated, BUT only if isOK = 1 on Day = 2.
The query for the following example table tablename
Name | Day | isOK
char | int | int
-----------------
Flo | 1 | 1
Seb | 1 | 1
Tim | 1 | 0
Flo | 2 | 1
Seb | 2 | 0
Tim | 2 | 1
should give Flo: 2 and Tim: 1, but not Seb: 1, since his isOK on Day = 2 is 0.
I've tried using SUM(isOK) with IF constructs, but it's just not working. My alternative solution, to select all Name where isOK = 1 first and select the SUM(isOK) for each of the names is slow and seems in need of improvement.
I guess it's not that difficult, but I've been trying for hours now and I just can't combine my two queries into one.
One way to do this is to use a conditional expression together with a having clause like this:
select name, sum(isOk) ok_sum
from your_table
group by name
having sum(case when day = 2 and isOK = 1 then 1 else 0 end) > 0;
With your sample data the result would be:
name ok_sum
Flo 2
Tim 1
As MySQL evaluates boolean expressions as 1 or 0 it should be possible to reduce the condition to this:
having sum(day = 2 and isOK = 1) > 0;
Another way to do it would be to use a correlated subquery that makes sure there exists a row with Day = 2 and isOk = 1 for the Name:
select t1.name, sum(t1.isOk) ok_sum
from your_table t1
where exists (
select 1
from your_table t2
where t2.day = 2 and t2.isOK = 1 and t1.name = t2.name
)
group by t1.name
See this fiddle
TRY this :
SELECT
name, SUM(isok) AS isOk
FROM
table
GROUP BY `name`
HAVING SUM(`day` = 2 AND isok = 1) > 0;
SELECT x.name, SUM(y.isOK) total
FROM my_table x
JOIN my_table y
ON y.name = x.name
WHERE x.day = 2
AND x.isok=1
GROUP
BY x.name;
I have simple mysql table with 2 fields:
atr_id | atr_val_id
1 | 100
1 | 200
1 | 300
2 | 100
3 | 100
3 | 200
4 | 200
How can I select for example all atr_ids values that have atr_val_id = 100 AND 200 and nothing more ? This will be only atr_id = 3 in that example.
Or for example only 200, this will be atr_id = 4
There could be be simpler ways to do. Here goes one solution that uses mix of group by, group_contact, distinct, sort and having clause to fetch your desired result
All atr_ids values that have atr_val_id = 100 AND 200 and nothing more. This will return only atr_id = 3
select
atr_id
from
tbl_test
group by
atr_id
having
group_concat(distinct atr_val_id order by atr_val_id asc) = '100,200'
and for only 200, this will return atr_id = 4
select
atr_id
from
tbl_test
group by
atr_id
having
group_concat(distinct atr_val_id order by atr_val_id asc) = '200'
Another way is to use the NOT EXISTS as below
select atr_id
from table_name t1
where atr_val_id in(100,200)
AND NOT EXISTS
(
select 1
from table_name t2
where t2.atr_id = t1.atr_id
group by t2.atr_id
having count(t2.atr_val_id) < 2 OR count(t2.atr_val_id) > 2
)
group by t1.atr_id
;
You can change the count part depending on the number of atr_val_id you are looking at. Say for just 200 its going to be
having count(t2.atr_val_id) < 1 OR count(t2.atr_val_id) > 1
And subsequently you need to change the IN clause as well.
I am writing a query to grab the items that a specific user_id was the first to use. Here is some sample data -
item_id used_user_id date_used
1 1 2012-08-25
1 2 2012-08-26
1 3 2012-08-27
2 2 2012-08-27
3 1 2012-08-27
4 1 2012-08-21
4 3 2012-08-24
5 3 2012-08-23
query
select item_id as inner_item_id, ( select used_user_id
from test
where test.item_id = inner_item_id
order by date_used asc
limit 1 ) as first_to_use_it
from test
where used_user_id = 1
group by item_id
It returns the correct values
inner_item_id first_to_use_it
1 1
3 1
4 1
but the query is VERY slow on a giant table. Is there a certain index that I can use or a better query that I can write?
i can't get exactly what you mean because in your inner query you have sorted it by their used_user_id and and on your outer query you have filtered it also by their userid. Why not do this directly?
SELECT DISTINCT item_id AS inner_item_id,
used_user_id AS first_to_use_it
FROM test
WHERE used_user_id = 1
UPDATE 1
SELECT b.item_id,
b.used_user_id AS first_to_use_it
FROM
(
SELECT item_ID, MIN(date_used) minDate
FROM tableName
GROUP BY item_ID
) a
INNER JOIN tableName b
ON a.item_ID = b.item_ID AND
a.minDate = b.date_used
WHERE b.used_user_id = 1