Complicated MySQL Query taking forever to execute - php

I've a somewhat suboptimal database structure I have to work with. Table1 contains 2 Ids (lets call them id1, id2). These Ids link to 2 rows in another table Table2. I want to get some columns of Table1 and use id1 and id2 to get columns out of Table2. Do I really have to join the same table twice to link to different rows or is there another more efficient way to do this? My queries take over 30 seconds for 20 rows.
SELECT t1.id1, t1.id2, t2.name, t3.name
FROM Table1 t1, Table2 t2, Table2 t3
WHERE t1.id1 = t2.id AND t1.id2 = t3.id AND t1.index = 2

Not tested, but it should be possible to use JOINS:
SELECT t1.id1, t1.id2, t2.name FROM
FROM Table1 t1
JOIN Table2 t2
ON t1.id1 = t2.id OR t1.id2 = t2.id
WHERE t1.index = 2

If you really need this Many-to-Many relation, please try JOIN:
SELECT t2.id AS t2_id,
t3.id AS t3_id,
t2.name AS t2_name,
t3.name AS t3_name
FROM Table1 t1
JOIN Table2 t2
ON t1.id1 = t2.id
JOIN Table3 t3
ON t1.id2 = t3.id
WHERE t1.index = 2

Related

MySQL two-table INNER JOIN , LEFT JOINED onto third table with only one row with lowest value

I searched around and found a near example to what I'm looking for, but it doesn't work in my case.
I have a query that does an INNER JOIN on two tables and this join constrains my overall data set substantially. I then want to LEFT JOIN onto a third table but I only want one record from that third table. The reason for the left join is because not every result of the INNER JOIN has a match in the 3rd table. Something like this:
SELECT DISTINCT t1.code, t2.id, t2.code, t3.id, t3.source_title, t3.display_order
FROM table1 t1
INNER JOIN table2 t2 ON t2.code=t1.code AND t2.type=0
LEFT JOIN table3 t3 ON t3.code=t1.code
ORDER BY t1.code, t3.display_order
This query returns too many records because the third table contains multiple records with a matching code. I just want the first one that matches with the lowest display_order value and, unfortunately, I can't limit the records to have display_order=1 because the lowest display order is not always one.
IMPORTANT: The t3.id value (if any) returned by this query must correspond to the record with the lowest display_order value. I.e., it won't work if the query correctly returns the lowest display_order value but the t3.id value corresponds to some other record in table 3.
Is this even possible? Any help would be much appreciated.
EDIT: Per Nick's suggestion, I have tried this, which appears to be working. I'll do some verification and report back:
SELECT DISTINCT t1.code, t2.*, sq.id, sq.source_title, sq.display_order
FROM table1 t1
INNER JOIN table2 p ON t2.code=t1.code AND t2.type=0
LEFT JOIN (
SELECT t3.*
FROM table3 t3
WHERE t3.display_order=(
SELECT MIN(display_order)
FROM table3 t3a
WHERE t3a.code = t3.code
)
) sq ON sq.code=t1.code
ORDER BY t1.code, sq.display_order
You should be able to replace table3 in your LEFT JOIN with
(SELECT *
FROM table3 t3
WHERE display_order = (SELECT MIN(display_order)
FROM table3 t3a
WHERE t3a.code = t3.code)
) t3
In MySQL 8.0 you can try to use row_number() for each code and ordered by display_order in a subquery from table3. Then left join that result and check for the row_number() to be equal to 1.
SELECT DISTINCT
t1.code,
t2.id,
t2.code,
t3.id,
t3.source_title,
t3.display_order
FROM table1 t1
INNER JOIN table2 t2
ON t2.code = t1.code
LEFT JOIN (SELECT t3.id,
t3.source_title,
t3.display_order,
t3.code,
row_number() OVER (PARTITION BY t3.code
ORDER BY t3.display_order) rn
FROM table3 t3) t3
ON t3.code = t1.code
WHERE t2.type = 0
AND t3.rn = 1
ORDER BY t1.code,
t3.display_order;
In lower versions you can try correlated subqueries ordered by display_order and LIMIT 1 (to get only one record).
SELECT DISTINCT
t1.code,
t2.id,
t2.code,
(SELECT t3.id
FROM table3 t3
WHERE t3.code = t1.code
ORDER BY t3.display_order,
t3.id
LIMIT 1) id,
(SELECT t3.source_title
FROM table3 t3
WHERE t3.code = t1.code
ORDER BY t3.display_order,
t3.id
LIMIT 1) source_title,
(SELECT t3.display_order
FROM table3 t3
WHERE t3.code = t1.code
ORDER BY t3.display_order,
t3.id
LIMIT 1) display_order
FROM table1 t1
INNER JOIN table2 t2
ON t2.code = t1.code
WHERE t2.type = 0
ORDER BY t1.code,
(SELECT t3.display_order
FROM table3 t3
WHERE t3.code = t1.code
ORDER BY t3.display_order,
t3.id
LIMIT 1);
I assumed, that display_order in table3 isn't unique but id is. So I added id to the ORDER BY clauses in the subqueries to make sure the same record is selected in each of them. If display_order is unique, you can remove id FROM the ORDER BY clauses.
Edit:
If you don't want to repeat the subqueries in the (overall) ORDER BY clause, you can also order by the column ordinals. E.g.:
...
ORDER BY 1, 6;

COUNTING mysql table rows based on 2 conditions from another table

I have two tables table1 and table2. table1 has columns id and table2_id while table2 has id and category. I need to count rows from table1 based on two separate values in table2.category containing value Regular or Special.
I have done this in two queries but I want to know if it is possible in a single sql. My queries are:
"SELECT COUNT(t1.id) AS regular FROM table1 t1 LEFT OUTER JOIN table2 t2 ON t1.t2_id = t2.id WHERE t2.category = 'Regular'";
"SELECT COUNT(t1.id) AS special FROM table1 t1 LEFT OUTER JOIN table2 t2 ON t1.t2_id = pr.id WHERE t2.category = 'Special'";
Thanks.
EDIT
The second query JOIN should read ON t1.t2_id = t2.id and not ON t1.t2_id = pr.id. Sorry for the confusion that may have caused. Please update/edit your answers/comments accordingly.
Move the Where condition to CASE statement and do the counting
Here is one way using Conditional Aggregate
SELECT
COUNT(case when t2.category = 'Regular' then t1.id end) AS Regular,
COUNT(case when t2.category = 'Special' then t1.id end) AS special
FROM table1 t1
INNER JOIN table2 t2 ON t1.t2_id = pr.id
Where t2.category IN ('Regular','Special' )
Note : I have changed the LEFT JOIN to INNER JOIN because you want to count only when table2.category is 'Regular' or 'Special' so no use of LEFT JOIN here
Instead of
"SELECT COUNT(t1.id) AS regular FROM table1 t1 LEFT OUTER JOIN table2 t2 ON t1.t2_id = t2.id WHERE t2.category = 'Regular'";
"SELECT COUNT(t1.id) AS special FROM table1 t1 LEFT OUTER JOIN table2 t2 ON t1.t2_id = pr.id WHERE t2.category = 'Special'";
you can do this:
select t2.category, count(t1.id)
from table1 t1
left outer join table2
on t1.t2_id = t2.id
group by t2.category
having t2.category in ('Regular', 'Special')
The suggested query groups the joined records, filters the groups and selects the category name and its count.

MySQL: If a column value is 2 THEN do X ELSE do Y

The database I am working on right now is somewhat messy. I have three potential tables, that I want to join but in some cases it may only be two tables.
Let's call these table1, table2 and table3.
table1 has a field called "type". If table1.type is 2, then I only need to join table3.
For any other values I want to join table2 and then table3.
How can I achieve this in one single SQL query rather than: 1) having one query to select the type. 2) make a PHP foreach-loop to check the type of the current iteration and 3) perform a new query according to the type value.
Edit:
I'll try to be more specific.
table1 has a column named "pid" that references to a whole other table, but that's redundant to this question. I tried working my ways around with UNIONs and LEFT JOINs but couldn't manage to achieve what I was looking for.
I want to select all results from my database with the "pid" value being "100". This gives me four rows in return, where was 2 of them are of type value "2" and the others are "1".
So basically what I want to achieve is the following two SQL statements in one:
(If "type" is "2")
SELECT *
FROM table1 t1
INNER JOIN table3 t3
ON t1.id = t3.t1_id
WHERE t1.pid = 100
(If "type" is NOT "2")
SELECT *
FROM table1 t1
INNER JOIN table2 t2
ON t1.id = t2.t1_id
INNER JOIN table3 t3
ON t2.id = t3.t2_id
WHERE t1.pid = 100
I'm guessing I could manage to do this with a UNION statement, but I'm confused on how to implement the WHERE t1.pid = '100' part.
use an UNION e.g.
SELECT t1.*, t3.*
FROM table1 t1
INNER JOIN table3 t3 ON t1.id = t3.t1_id
WHERE t1.pid = 100 and t1.type = 2
UNION
SELECT t1.*, t3.*
FROM table1 t1
INNER JOIN table2 t2
ON t1.id = t2.t1_id
INNER JOIN table3 t3 ON t2.id = t3.t2_id
WHERE t1.pid = 100 and t1.type <> 2;
but it would be better to explicitly name the columns you want to get back.

select from table only if the id exist in that table mysql pdo php

I want to create a select query using join on 3 or more tables. I have 3 tables namely t1, t2, t3 and a common column id existing in all 3 tables. I want to select the 3 table if the id exists in the table my query is like this.
Select * from t1
inner join t2 on t1.id = t2.id
inner join t3 on t2.id = t3.id
where t1.id = 1 and t2.id = 1 and t3.id = 1
the query is returning values if the id exists in all the 3 tables. But if it is not in any table example t3 i will not return anything. I am looking for a way that if it does not exist in t3 it should i proceed to just select from t1 and t2
Is this what you need?
SELECT *
FROM t1
INNER JOIN t2 on t1.id = t2.id
LEFT JOIN t3 on t2.id = t3.id
WHERE t1.id = 1 AND t2.id = 1 AND (t3.id = NULL OR t3.id = 1)
SELECT * FROM t1 JOIN t2 ON t1.id = t2.id
LEFT JOIN t2 ON t1.id= t2.id WHERE t1.id= 1
This will return null for t3 columns if id is not present in t3
I think these sql wil helpful to you
Select * from t1,t2,t3
where t1.id = 1 or t2.id = 1 0r t3.id = 1
these sql also will useful to you
SELECT * FROM t1 WHERE t1.id=1
UNION ALL
SELECT * FROM t2 WHERE t2.id=1
UNION ALL
SELECT * FROM t2 WHERE t2.id=1
Thank you.

Search Function is Hang with 2 year data and join

here us the query
SELECT *
FROM Table1
WHERE complete='Y'
AND shipped='Y'
AND active='Y'
AND create_dttm > '2013-10-10 08:28:41'
AND order_id IN
(SELECT DISTINCT t1.order_id
FROM Table2 t1
INNER JOIN table3 t2 ON t1.prod_id = t2.prod_id
WHERE t2.prod_sku LIKE '%D-600%'
AND t1.create_dttm > '2013-02-15 08:28:41')
You are using a sub-query in WHERE clause, that could be the main reason behind slow execution of your query. Try using JOINS instead of sub query.
SELECT t1.*
FROM Table1 t1
INNER JOIN Table2 t2 ON T1.order_id = T2.order_id
AND t2.create_dttm > '2013-02-15 08:28:41'
INNER JOIN table3 t3 ON t2.prod_id = t3.prod_id
AND t3.prod_sku LIKE '%D-600%'
WHERE complete='Y'
AND shipped='Y'
AND active='Y'
AND create_dttm > '2013-10-10 08:28:41'
And also check for indexes on your tables.

Categories