Is it optimal to use "OR" for multiple sub-queries? - mysql

I have a slow query of the following form.
SELECT DISTINCT * FROM table e
WHERE e.eid in (select eid from view1)
OR e.eid in (select eid from view1)
OR e.eid in (select eid from view2)
OR e.eid in (select eid from view3)
OR e.eid in (select eid from view4)
OR e.eid in (select eid from view5)
I am new to MySQL but is there a faster way to make this query?

May be using UNION like this:
SELECT DISTINCT * FROM table e
WHERE e.eid IN
(
SELECT eid FROM view1
UNION
SELECT eid FROM view2
UNION
SELECT eid FROM view3
UNION
SELECT eid FROM view4
UNION
SELECT eid FROM view5
)

you can use Union in your query .
SELECT DISTINCT e.*
FROM table e
INNER JOIN
(
SELECT eid FROM view1
UNION ALL
SELECT eid FROM view2
UNION ALL
SELECT eid FROM view3
UNION ALL
SELECT eid FROM view4
UNION ALL
SELECT eid FROM view5
) AS B ON e.eid = B.eid
you should use join query instead of using IN query . IN query will run for each rows of the table so it will take more time . Join query will run only one time .

Using id IN (unioned subquery) generally won't perform well.
Try using a standard join on a unioned subquery:
SELECT a.*
FROM (
SELECT eid FROM view1
UNION
SELECT eid FROM view2
UNION
SELECT eid FROM view3
UNION
SELECT eid FROM view4
UNION
SELECT eid FROM view5
) e
JOIN table a ON a.id = e.eid
The important thing here is that the ids from the views come first in the FROM table list, so that table can be accessed efficiently, not the other way around when the in indexed view result would have to be table-scanned for every row of table.
Also note there is no need for DISTINCT because the UNION inside the subquery eliminated duplicates.
This query should perform very well.

Related

Merging multiple similar query

I can get the result from multiple but simple queries but I would like to merge them further as sub queries.
All sub queries are going to be similar to this one-
SELECT COUNT(count) AS acc1 FROM (SELECT COUNT(table.colname) AS count
FROM tablename GROUP BY tablename.sumcol HAVING count=1) as access1
likewise others will be
SELECT COUNT(count) AS acc2 FROM (SELECT COUNT(table.colname) AS count
FROM tablename GROUP BY tablename.sumcol HAVING count=2) as access2
You could use UNION ALL for get both the results in the same result set
SELECT 'acc1' , COUNT(count)
FROM ( SELECT COUNT(table.colname) AS count
FROM tablename G
ROUP BY tablename.sumcol
HAVING count=1) access1
UNION ALL
SELECT 'acc2', COUNT(count)
FROM (SELECT COUNT(table.colname) AS count
FROM tablename
GROUP BY tablename.sumcol
HAVING count=2) access2
I have addedd 'acc1' and 'acc2' literal value for a better result reading but you can avoid it
do the fact you have only a rows for both the query , if you need the two result on the same row, you can use a cross join (cartesian product)
SELECT COUNT(count) as count_acc1, T.count2
FROM ( SELECT COUNT(table.colname) AS count1
FROM tablename G
GROUP BY tablename.sumcol
HAVING count=1) access1
CROSS JOIN (
SELECT COUNT(count) as count2
FROM (SELECT COUNT(table.colname) AS count
FROM tablename
GROUP BY tablename.sumcol
HAVING count=2) access2 ) T

MySQL join to find non-matches or possibly a full outer join with exclusion

I have 2 identical dead-simple tables in a MySQL database with different data. I need a single query that will return all the results that aren't a duplicate.
Here's an example:
Table 1. (column "item")
a
b
c
d
Table 2. (column "item")
a
b
e
f
x
Wanted Result
c
d
e
f
x
Try this -
SELECT * FROM TABLE1 WHERE ITEM NOT IN (SELECT ITEM FROM TABLE2)
UNION
SELECT * FROM TABLE2 WHERE ITEM NOT IN (SELECT ITEM FROM TABLE1)
You can use NOT EXISTS, e.g.:
SELECT item
FROM table1 t1
WHERE NOT EXISTS (
SELECT item FROM table2 WHERE item = t1.item
);
We could join them using a distinct an union ALL. then count and having.
the distinct is needed since we care about unique accross the sets.
SELECT item FROM (
SELECT distinct item
FROM tbl1
UNION ALL
SELECT distinct item
FROM tbl2) B
GROUP BY item
HAVING count(*) =1
SELECT * FROM TABLE1 WHERE ITEM NOT IN (SELECT ITEM FROM TABLE2)
union
SELECT * FROM TABLE2 WHERE ITEM NOT IN (SELECT ITEM FROM TABLE1)
Or another method would be to use an inner join to grab all the duplicate data and then pull all the data that isn't in your duplicate dataset.
SELECT * into #temptable FROM TABLE1 INNER JOIN TABLE2 on table2.x = table1.x
union
SELECT * into #temptable FROM TABLE2 INNER JOIN TABLE1 on table2.x = table1.x
SELECT * FROM Table1 WHERE NOT IN (SELECT * FROM #temptable)
UNION
SELECT * FROM Table2 WHERE NOT IN (SELECT * FROM #temptable)
A lot like xQbert's, but with the assumption that item is UNIQUE/PRIMARY...
SELECT a.*
FROM
( SELECT item FROM table1
UNION ALL
SELECT item FROM table2
) a
GROUP
BY item
HAVING COUNT(*) = 1;

SQL USING LIKE IN JOIN

I have a table Books
NAME
A
B
C
and another Booksassignment
ID ASSIGNMENTTEXT
1 Book.Assigned.To:A
2 Book.Assigned.To:A
3 Book.Assigned.To:B
4 Book.Assigned.To:B
5 Book.Assigned.To:C
6 Book.Assigned.To:A
7 OtherText:A
I want to get how many books were assigned to each person e.g.
I should get
Person Bookcount
A 3
B 2
C 1
I ONLY WANT TO GET THOSE ROWS WHERE Book.Assigned.To:NAME
I have tried using substring but I cannot solve
Thanks,
Aiden
You could use inner join and group by
select a.Name, b.count(*)
from Books a
inner join Booksassignment b on b.ASSIGNMENTTEXT like concat('%:', a.NAME)
group by a.Name
CREATE TABLE #T(nAME varchar(25))
INSERT INTO #T
SELECT 'A' UNION ALL
SELECT 'B' UNION ALL
SELECT 'C'
CREATE TABLE #tt (Id int,Book Varchar(25),Name Varchar(255))
INSERT INTO #tt
SELECT 1,'Book.Assigned','A' UNION ALL
SELECT 2,'Book.Assigned','A' UNION ALL
SELECT 3,'Book.Assigned','B' UNION ALL
SELECT 4,'Book.Assigned','B' UNION ALL
SELECT 5,'Book.Assigned','C' UNION ALL
SELECT 6,'Book.Assigned','A'
SELECT #tt.Name,COUNT(#tt.Name) from #tt INNER JOIN #T
ON #t.Name=#tt.Name
GROUP BY #tt.Name
I tried it on Mysql :
SELECT CONCAT(SUBSTRING(o.ASSIGNMENTTEXT,-1) ,' ', COUNT(ASSIGNMENTTEXT) ) FROM Booksassignment AS o GROUP BY ASSIGNMENTTEXT
If length of name column is always 1, you can as the below:
SELECT
B.Name AS Person,
COUNT(1) Bookcount
FROM
Books B INNER JOIN
Booksassignment BA ON B.NAME = BA.RIGHT(ASSIGNMENTTEXT, 1)
GROUP BY
B.Name
If name is allways in the end of the ASSIGNMENTTEXT column use right function
select name, count(*) from Books b
inner join Booksassignment a on b.name like RIGHT(a.ASSIGNMENTTEXT, 1)
where ASSIGNMENTTEXT like 'Book.Assigned.To:%'
group by name
Update ANSWER
If you need first column to be "Book.Assigned.To:":
select ASSIGNMENTTEXT, count(*) from Books b
inner join Booksassignment a on b.name like RIGHT(a.ASSIGNMENTTEXT, 1)
where ASSIGNMENTTEXT like 'Book.Assigned.To:%'
group by ASSIGNMENTTEXT
Exemple:
with Books as (select * from ( select 'A' as name union select 'B' union select 'C' ) as b )
, Booksassignment as (
select * from (
select 1 as iD, 'Book.Assigned.To:A' ASSIGNMENTTEXT union
select 2, 'Book.Assigned.To:A' union
select 3, 'Book.Assigned.To:B' union
select 4, 'Book.Assigned.To:B' union
select 5, 'Book.Assigned.To:C' union
select 6, 'Book.Assigned.To:A'
) as c
)
select name, count(b.name) as ASSIGNMENTs from Books b
inner join Booksassignment a on b.name like RIGHT(a.ASSIGNMENTTEXT, 1)
group by name
Try this:
;WITH T AS
(
SELECT
Id,
REPLACE(AssignmentText,SUBSTRING(AssignmentText,0, CHARINDEX(':',AssignmentText,0)+1),'') AS AssignedName
FROM #tblBooksassignment
)
SELECT
B.Name,
COUNT(T.Id) AS 'PersonBookCount'
FROM T
INNER JOIN #tblBooks B ON B.Name=T.AssignedName
GROUP BY B.Name
Output:
i think something like this will work
SELECT B.NAME,
COUNT( * ) as Bookcount
FROM Books B
JOIN Booksassignment BA
ON B.NAME = SUBSTRING( BA.ASSIGNMENTTEXT, -1 )
AND BA.ASSIGNMENTTEXT LIKE 'Book.Assigned.To:%'
GROUP BY B.NAME

Mysql UNION as a subquery with an alias field

I have a UNION query as bellow (I have simplified my working query so it is easier to read) :
SELECT count(*) FROM
(SELECT DISTINCT `tableA`.`Store Name` FROM `tableA` UNION SELECT DISTINCT `tableB`.`Store Name` FROM `tableB`) t
This works fine and results in a single number with column name COUNT(*)
I want to get this value as another column in another query so I do :
SELECT DISTINCT `tableC`.`id as PID,
(SELECT count(*) from (SELECT DISTINCT `tableA`.`Store Name` FROM `tableA` UNION SELECT DISTINCT `tableB`.`Store Name` FROM `tableB`) t) AS noofstores
WHERE
.....;
But it wont work! What am I doing wrong? This is part of a bigger query, and all the other subqueries work fine when I do
,
(SELECT .... ) AS column_name
,
Sorry for poor error description. Update :
This is my full query :
SELECT DISTINCT
`tableC`.`id` as PID,
(SELECT count(*)
from
(SELECT DISTINCT `tableA`.`Store Name` FROM `tableA` WHERE `tableA`.`id` = PID
union
SELECT DISTINCT `tableB`.`Store Name` FROM `tableB` WHERE `tableB`.`id` = PID) t) AS mycolumn_name
FROM
`tableC`
Looks like I had the union right and all, but the problem is the PID I am reffering to in the union :
1054 - Unknown column 'PID' in 'where clause'
So how do I solve this?
The PID column does not exist in the inner subquery, only in the outer query. Either you do an inner join in both of the queries in the union on tableC and do the filtering there, or you need to return the id column in the union queries and join the PID on them to do the filtering.
select tableC.id as PID, count(distinct storename)
from
(select distinct id, storename from tableA
union
select distinct id, storename from tableB) t1
inner join tableC on t1.id=tableC.id
group by tableC.id
You have to join thos two tables to get the result
SELECT DISTINCT c.id as PID from table C inner join
(SELECT count(*) from
(SELECT DISTINCT `tableA`.`Store Name` as st
FROM `tableA` UNION SELECT DISTINCT `tableB`.`Store Name` as st
FROM `tableB`) t on t.some_id=c.id WHERE

Making a select for a two select sql/oracle

I want to join join 2 select in single query :
Here are the two queries.
SELECT player_id, SUM(score) score
FROM (
SELECT id_p1 player_id, score_p1 score
FROM matchs
UNION ALL
SELECT id_p2, score_p2
FROM matchs
) q
GROUP BY player_id
AND
SELECT player_id, SUM(score) score
FROM (
SELECT id_p1 player_id, score_p2 score
FROM matchs
UNION ALL
SELECT id_p2, score_p1
FROM matchs
) q
GROUP BY player_id
Thank you !
Try this
SELECT table1.player_id, table1.score score1, table2.score score2,
abs(table1.score - table2.score) difference
FROM (
SELECT player_id, SUM(score) score
FROM (
SELECT player1_id player_id, score_p1 score FROM matchs
UNION ALL
SELECT player2_id , score_p2 FROM matchs
) q GROUP BY player_id
) table1
INNER JOIN
(
SELECT player_id, SUM(score) score
FROM (
SELECT player1_id player_id, score_p2 score FROM matchs
UNION ALL
SELECT player2_id , score_p1 FROM matchs
) q GROUP BY player_id
) table2 ON table1.player_id = table2.player_id
SQL Fiddle Demo
Ideally, one would perform this operation using a FULL JOIN:
SELECT COALESCE(t1.player1_id, t2.player2_id)
SUM(COALESCE(t1.score_p1,0) + COALESCE(t2.score_p2,0))
FROM table_name t1
FULL JOIN table_name t2 ON t1.player1_id = t2.player2_id
GROUP BY COALESCE(t1.player1_id, t2.player2_id)
However, sadly MySQL does not have native support for such a join operation. Instead, one can simulate it by making a UNION between a LEFT JOIN and a RIGHT JOIN, then aggregating:
SELECT p, SUM(s) FROM (
SELECT t1.player1_id p, SUM(t1.score_p1 + IFNULL(t2.score_p2,0)) s
FROM table_name t1
LEFT JOIN table_name t2 ON t1.player1_id = t2.player2_id
GROUP BY t1.player1_id
UNION
SELECT t2.player2_id, SUM(IFNULL(t1.score_p1,0) + t2.score_p2)
FROM table_name t1
RIGHT JOIN table_name t2 ON t1.player1_id = t2.player2_id
GROUP BY t2.player2_id
) t GROUP BY p
See it on sqlfiddle.