MySQL AND as optional - mysql

AND t1.Team IN (SELECT Team FROM t2 WHERE t3.ID = t2.ID)
How can I make above AND as optional, if the sub-query does not have any results, do not even consider AND t1.Team IN ("").
Something like this does not work:
AND IF((SELECT Team FROM t2 WHERE t3.ID = t2.ID), (t1.Team IN (SELECT Team FROM t2 WHERE t3.ID = t2.ID)), 1)

Formally you need in
AND ( t1.Team IN (SELECT Team FROM t2 WHERE t3.ID = t2.ID)
OR NOT EXISTS (SELECT Team FROM t2 WHERE t3.ID = t2.ID)
)
But moving this subquery to FROM clause with proper left-joining seems to be more reasonable in your case.

You can use Case Statement for check if else condition inside the SQL query syntax.
For that I think your query looks like below:
AND
CASE
WHEN (SELECT COUNT(*) FROM t2 WHERE t3.ID = t2.ID) > 0 THEN t1.Team IN (SELECT Team FROM t2 WHERE t3.ID = t2.ID)
ELSE 1=1
END

Might be bit faster to do (if t1.Team is not NULL):
AND t1.Team IN (
SELECT Team FROM t2 WHERE t3.ID = t2.ID
UNION
SELECT t1.Team
)

Here's a nice trick to keep the query simple and good performance:
AND IFNULL(
(
SELECT MAX(IIF(t1.Team = t2.Team, 1, 0))
FROM t2
WHERE t3.ID = t2.ID
), 1) = 1
explanation:
this sub-query does the following:
compare all rows in t2 to t1 by Team
if any matching Team- will return 1, else 0
if no rows at all in t2- will return NULL
the IFNULL will transform result of NULL to 1
so we expect the result of the sub-query to be 1
This means 1 will be returned from sub-query in 2 cases:
if no rows are found in t2
or if a matching Team found in t2

Related

Using values from main query inside a subquery

I had a problem creating a MySQL query with a subquery.
I wanted to use some data from the main query on the subquery, as many times did.
But this time I wanted to use it in a JOIN and didn't worked. I really want to understand why this happens.
I will show you some examples that works and the one that didn't.
I made this simple structure to reproduce the example:
# table1
id field1
1 *first_value*
2 *another_value*
#table2
id field2
1 *second_value*
Using table1.id on the WHERE of the subquery to get a value, the most typical use for me (I know this can be a join, but i try to show the difference):
SELECT
t1.field1,
(
select t2.field2
FROM table2 as t2
WHERE t2.id = t1.id
) as field2
FROM table1 as t1
WHERE t1.id = '1';
You can use table1.id on the SELECT part too (not much sense in the example, but works):
SELECT
t1.field1,
(
select t1.id as field2
FROM table2 as t2
WHERE t2.id = t1.id
) as field2
FROM table1 as t1
WHERE t1.id = '1';
Now, if you try to use it on a JOIN inside the subquery, then, crashes:
SELECT
t1.field1,
(
select t1.id
FROM table2 as t2
LEFT JOIN table1 as t3 ON t3.id = t1.id
WHERE t2.id = t1.id
) as field2
FROM table1 as t1
WHERE t1.id = '1';
Kernel error: Error( 1054 ) 42S22: "Unknown column 't1.id' in 'on clause'"
Buuut, u can do the JOIN using the field in another subquery changing ON t3.id = t1.id to ON t3.id = (SELECT t1.id) ???
SELECT
t1.field1,
(
select t1.id
FROM table2 as t2
LEFT JOIN table1 as t3 ON t3.id = (SELECT t1.id)
WHERE t2.id = t1.id
) as field2
FROM table1 as t1
WHERE t1.id = '1'
I wonder to know why the third example query doesn't work while all others does.
Can someone explain this, please?
Thank you :)
That's because all elements in the ON clause of a JOIN, must belong the one of the joined tables, so as your t2.id must be equal to t1.id, you can do
SELECT
t1.field1,
(
select t1.id
FROM table2 as t2
LEFT JOIN table1 as t3 ON t3.id = t2.id
WHERE t2.id = t1.id
) as field2
FROM table1 as t1
WHERE t1.id = '1';

RoR/MySQL removing DISTINCT and ORDERING doesn't make any result

I have a query
SELECT DISTINCT t1.country
FROM table2 t2
JOIN table3 t3
ON t2.table3_id = t3.id
JOIN table4 t4
ON t3.table4_id = t4id
LEFT
JOIN table1 t1
ON t1.table2_id = t2.id
WHERE t2.type IN ('Some')
AND t4.locale_id = 11
AND t1.table2_id IS NOT NULL
AND t1.country IS NOT NULL
AND t1.country != ''
ORDER
BY t1.country ASC
And when I remove Distinct and ordering it works much faster in mysql console, BUT it working same time when I run it through Rails ActiveRecord:
ActiveRecord::Base.connection.execute(query)
So that I have two questions.
First and main - why optimization hasn't result in Rails Environment?
Second - Do you know how to speed up this query more?

How do I make aggregate query return empty set instead of NULL row?

I have a SQL query like this:
SELECT t1.name, MAX(t2.value)
FROM t2
JOIN t1 ON t1.id = t2.t1_id
WHERE t2.t1_id = 1 AND t2.text_id = 16;
However, when t2 selection is empty, it returns a row containing NULL values (because of MAX function returning NULL when called on an empty set). I would like it to return an empty set instead. How can I achieve it?
Having clause fits perfectly here:
SELECT
t1.name, MAX(t2.value)
FROM
t2 JOIN t1 ON t1.id = t2.t1_id
WHERE
t2.t1_id = 1 AND t2.text_id = 16
-- GROUP BY something?
HAVING
MAX(t2.value) IS NOT NULL
Try this in sql server ...
with cte as
(
SELECT t1.name, MAX(t2.value) a
FROM t2
JOIN t1 ON t1.id = t2.t1_id
WHERE t2.t1_id = 1 AND t2.text_id = 16;
)
select * from cte where a is not null
try this in Mysql
select p.* from
(
SELECT t1.name, MAX(t2.value) a
FROM t2
JOIN t1 ON t1.id = t2.t1_id
WHERE t2.t1_id = 1 AND t2.text_id = 16;
) p where p.a is not null
Simply RETURN when you don't want to get a resultset.
IF ((SELECT MAX(t2.Value) FROM t2) > 0)
SELECT t1.name, MAX(t2.value)
FROM t2
JOIN t1 ON t1.id = t2.t1_id
WHERE t2.t1_id = 1 AND t2.text_id = 16
ELSE
RETURN
Totally agree with comment by Gordon Linoff.
To wrap select query in another select query is good idea but when we have many aggregate fields then it will become cumbersome to do and think about that you have to make change like this to more than 10 queries.
You should use GROUP BY clause to query which will help you to fix your mentioned issue but more than that it will help you to get aggregate values(field wise) based on group by clause. In your case you should use GROUP BY t1.name can work.
So your aggregate result set will be group by name.
If you do not use GROUP BY then think that you have name field which have 100 unique values but you'll only 1 row which all rows aggregated data which may be wrong implementation.
Here's some more details about aggregate functions and group by.

How to count and get result of two rows in Mysql Select Query?

This is my table structure.
sometime table1 data my repeat (Ex : Actually Id 1 should have only 4 rows but sometime it is 8 due to duplication) so avoid duplication I use GROUP BY command in select query
Table 1
|id| website|time|
-----------------
|01|facebook|20.0|
|01|google |40.0|
|01|youtube |10.0|
|01|ebay |30.0|
|02|facebook|50.0|
|02|ebay |50.0|
Table 2
|id|marks|
-----------
|01| 80|
|02| 90|
|03| 70|
|04| 100|
I want to select (marks),(time on facebook) and (count of time on google & youtube) of specific user
Following select query gives (marks),(time on facebook) of user id '01'
How to receive count of time of both google and youtube of id'1' in same query ?
SELECT table2.id,table2.marks, table1.time
FROM table1
RIGHT JOIN table2 ON table1.id= table2.id
WHERE table1.website LIKE ('%facebook%')
AND table1.id= '01'
GROUP BY table1.id, table1.website
You want to find the time on facebook and then the sum of youtube and google for a particular user you can use the mysql conditional sum to achieve it
select
sum(case when t1.website = 'facebook' then t1.time else 0 end) as `fb_time`,
(
sum(case when t1.website='google' then t1.time else 0 end)+
sum(case when t1.website='youtube' then t1.time else 0 end)
)
as `google_youtube`,
t2.marks
from table1 t1
join table2 t2 on t1.id = t2.id
where t1.id = '01'
If you need to calculate the same for all the users then you can do it as
select
t1.id,
sum(case when t1.website = 'facebook' then t1.time else 0 end) as `fb_time`,
(
sum(case when t1.website='google' then t1.time else 0 end)+
sum(case when t1.website='youtube' then t1.time else 0 end)
)
as `google_youtube`,
t2.marks
from table1 t1
join table2 t2 on t1.id = t2.id
group by t1.id
If I understand your query correctly, I think you will need to use a subquery.
The following subquery returns two counts; time_on_facebook & time_on_google_and_youtube
for all users
SELECT t1.id, t2.marks,
COUNT(t1.time) as time_on_facebook,
(SELECT COUNT(t1_sq.time)
FROM `table1` as t1_sq
WHERE (t1_sq.website = "youtube" OR t1_sq.website = "google")
AND t1_sq.id = t1.id
GROUP BY t1.id) as time_on_google_and_youtube
FROM `table1` as t1
LEFT JOIN table2 t2 ON t2.id = t1.id
WHERE t1.website = "facebook"
GROUP BY t1.id
To restrict it to user id = 01, add in a WHERE clause
SELECT t1.id, t2.marks,
COUNT(t1.time) as time_on_facebook,
(SELECT COUNT(t1_sq.time)
FROM `table1` as t1_sq
WHERE (t1_sq.website = "youtube" OR t1_sq.website = "google")
AND t1_sq.id = t1.id
GROUP BY t1.id) as time_on_google_and_youtube
FROM `table1` as t1
LEFT JOIN table2 t2 ON t2.id = t1.id
WHERE t1.website = "facebook" AND t1.id = 1
GROUP BY t1.id
Are you sure you want COUNT(time) or do you want SUM(time)?
Lastly, consider adding a primary key to both tables and maybe rename the "id" column to "user_id" for clarity.
Its not clear what you want the output to look like. I made a query,
but did not try it. Try it and let me know if it works.
select t1.id, t1.website, sum(t1.time) as total_time, max(t2.marks) as marks
from table1 as t1
left join table2 as t2
on t1.id = t2.id
where t1.website = 'facebook'
and t1.id = '01'
group by t1.id, t1.website
UNION
select t1.id, t1.website, sum(t1.time) as total_time, max(t2.marks) as marks
from table1 as t1
left join table2 as t2
on t1.id = t2.id
where t1.website IN ('youtube', 'google')
and t1.id= '01'
group by t1.id, t1.website

SQL trouble with COUNT

I have some SQL code that returns me some data from DB
SELECT t1.id as id, title, description FROM table1 t1
JOIN table2 t2 ON t1.id = t2.t1_id
WHERE t2.t3_id IN( SELECT id FROM table3 WHERE parent_id IN ( SELECT id FROM table3 WHERE parent_id = 1)) GROUP BY t1.id
I have some problem with counting number of rows of result. I know that I have to write almost the same code but with COUNT but I have there A problem, my code doesn't return me a number of rows.
Just use the COUNT(*) function. Also, your subqueries can be converted to a JOIN (and your sub-subquery is redundant):
SELECT COUNT(*)
FROM table1 t1
JOIN table2 t2
ON t1.id = t2.t1_id
JOIN table3 t2
ON t3.id = t2.t3_id
WHERE t3.parent_id = 1