Querying according to the newest related object - php

I have two entities with an 1-N relation, like this :
table_a
-------
id
name
table_b
------
id
table_a_id
name
status
created_at
I'm looking for a way in MySQL and especially with Doctrine ORM to query table_a with a "where" clause on table_b that affect only the last associated table_b record.
Supposing I have the following records :
table_a
----------------------------
id | name
----------------------------
1 | john
2 | mary
3 | chuck
table_b
--------------------------------------------------
id | table_a_id | name | status | created_at
--------------------------------------------------
1 | 1 | blue | 1 | 2000-01-01
2 | 1 | red | 1 | 2012-12-31
3 | 2 | yellow | 1 | 2000-01-01
4 | 2 | green | 0 | 2012-12-31
So I want to tell MySQL/Doctrine :
GIVE ME the table_a records
WHICH HAVE table_b records
AND status = 1 ON the last related elements (according to the created_at field)
This should only return :
table_a
----------------------------
id | name
----------------------------
1 | john

According to the book SQL Antipatterns, this type of join with the proper indexes can often perform better than a subquery. So, try this method out too:
SELECT a.*
FROM table_a a
JOIN table_b b1
ON b1.table_a_id = a.id
AND b1.status = 1
LEFT JOIN table_b b2
ON b2.table_a_id = a.id
AND b2.created_at > b1.created_at
WHERE b2.table_a_id IS NULL
If there could be two rows from table_b with status 1 that have the same table_a_id and created_at date, then you will need DISTINCT to avoid duplicates:
SELECT DISTINCT a.*
FROM table_a a
JOIN table_b b1
ON b1.table_a_id = a.id
AND b1.status = 1
LEFT JOIN table_b b2
ON b2.table_a_id = a.id
AND b2.created_at > b1.created_at
WHERE b2.table_a_id IS NULL

Not sure about the "Doctrine", but a MySQL query could be
select
a.ID,
a.Name
from
table_b B
join table_a A
on B.table_a_id = A.ID
where
B.status = 1
order by
B.Created_At DESC
limit 1

Related

How do I select distinct rows and cross check in another table?

I have 3 tables, users and tasks and completed_tasks. So basically I want to select all tasks where the user_id = 2 AND also check that the task does not exist in another table` the So here is my tables:
users table:
+----+-------+
| id | name |
+----+-------+
| 1 | John |
| 2 | Sally |
+----+-------+
tasks table:
+----+-----------+---------+
| id | task_name | user_id |
+----+-----------+---------+
| 1 | mop floor | 2 |
| 2 | dishes | 1 |
| 3 | laundry | 2 |
| 4 | cook | 2 |
+----+-----------+---------+
completed_tasks table:
+----+---------+---------+
| id | task_id | user_id |
+----+---------+---------+
| 1 | 1 | 2 |
+----+---------+---------+
Here is my current SELECT code for my MySQL database:
$db = "SELECT DISTINCT tasks.task_name, users.name FROM tasks LEFT JOIN ON users.id = tasks.user_id WHERE tasks.user_id = 2";
THe problem I'm having is: I want it to search in completed_tasks table and if the task exists, then don't select that task.
I tried to do that by adding the following but it did not work:
LEFT JOIN completed_tasks ON completed_tasks.user_id = 2
That did not work because if I had multiple completed tasks, it would just ignore it all together.
I want the end result should return the user's name and task name of task 3 and 4.
Also, performance is critical in my application. I could use PHP and loop through the arrays and do SELECT for each of them but that would not be good for performance.
You have a few ways to do this.
You can use a LEFT JOIN and then check for NULL in the optional table.
SELECT a.name, b.task_name
FROM users a
JOIN tasks b ON a.id = b.user_id
LEFT JOIN completed_tasks c ON c.task_id = b.id AND c.user_id = b.user_id
WHERE c.id IS NULL
;
You can do a NOT EXISTS sub-query
SELECT a.name, b.task_name
FROM users a
JOIN tasks b ON a.id = b.user_id
WHERE NOT EXISTS (
SELECT 1
FROM completed_tasks c
WHERE c.task_id = b.id AND c.user_id = b.user_id)
;

How to delete a row in a mysql db with inner / left join only if there is no NULL value

I've got one table which looks like this:
id_1 | col_1 | col_2
1 | 450 | 2018-01-30 21:38:00
2 | 111 | 2018-01-30 22:18:00
3 | 222 | 2018-01-31 22:18:00
and the second table looks like this:
id_2 | id_1 | beginn | outdated
1 | 1 | 2018-01-30 11:38:00 | 0
2 | 1 | 2018-01-30 12:18:00 | 1 <===== THIS ROW IS OUTDATED
3 | 1 | 2018-01-30 13:38:00 | 0
4 | 2 | 2018-01-30 14:18:00 | 0
5 | 3 | 2018-01-30 15:38:00 | 1 <===== THIS ROW IS OUTDATED
6 | 2 | 2018-01-30 16:18:00 | 0
How can I delete the values from table1 and table2 (with one single query) only if there will not be any valid join. e.g. delete from table1 the row with id_1=3 and from table2 the row with id_2=5 but NOT the row in table1 with id_1=1 because there are still entries in table2 which can be joined with.
I tried the following, but it will not work:
DELETE a,b FROM table1 a
LEFT JOIN table2 b on a.id_1=b.id_1
WHERE b.outdated=1
AND NOT EXISTS (
SELECT c.id_1 from table1 c
INNER JOIN table2 d on c.id_1=d.id_1
WHERE d.outdated=0)
How can I change my query or which FASTER query can be choosen for my intention
I'm not sure there's a way to do this in one query. This comes close:
DELETE a, b
FROM Table1 AS a
LEFT JOIN Table2 AS b ON a.id_1 = b.id_1
LEFT JOIN Table2 AS c ON a.id_1 = c.id_1 AND c.outdated = 0
WHERE c.id_1 IS NULL
AND (b.id_1 IS NULL -- no match
OR b.outdated = 1)
but it doesn't delete id_2 = 2 from Table2.
I think it may need to be done in two queries: First delete all rows in Table1 that don't join with any outdated = 0 rows in Table2, then delete all the outdated rows in Table2.
DELETE a
FROM Table1 AS a
LEFT JOIN Table2 AS b ON a.id_1 = b.id_1 AND b.outdated = 0
WHERE b.id_1 IS NULL;
DELETE FROM Table2
WHERE outdated = 1;
Or you can reverse the order, then you don't need to check b.outdated at all:
DELETE FROM Table2
WHERE outdated = 1;
DELETE a
FROM Table1 AS a
LEFT JOIN Table2 AS b ON a.id_1 = b.id_1
WHERE b.id_1 IS NULL;

PDO Query : Count related and repeated values then inner join

I work with PHP and PDO.
So I have 2 tables like,
Table 1
| id | name | age |
| 1 | John | 25 |
| 2 | Tom | 32 |
| 3 | James| 45 |
Table 2
| id | Comment | Link |
| 1 | some text | 3 |
| 2 | some text | 3 |
| 3 | some text | 1 |
So, Link column numbers represent id's in table1. For example Link = 3s in table 2 represent James in table 1. I need a query which brings all table1's data and also a number of repeated value for related Link column which comes from table2.
For example, the query should give me (let's choose James),
| id | name | age | Value |
| 3 | James | 45 | 2 |
value=2, because there are two 3s in link column which related to James
I tried somethings but got lots of errors.
I think you just need the GROUP BY
SELECT a.id,
a.name,
a.age,
count(*) as value
FROM table1 a
JOIN table2 b ON a.id = b.link
GROUP BY a.id, a.name, a.age
If you really want just one row then add WHERE
SELECT a.id,
a.name,
a.age,
count(*) as value
FROM table1 a
JOIN table2 b ON a.id = b.link
WHERE a.name = 'James'
GROUP BY a.id, a.name, a.age
or use subquery
SELECT a.id,
a.name,
a.age,
(SELECT count(*) FROM table2 b WHERE a.id = b.link) as value
FROM table1 a
WHERE a.name = 'James'

How to subtract values of two different columns from two different tables?

Example Table Structure
Table 1
ID | Name | Price
-----------------------------
1 | Casio | 30
2 | Titan | 40
Table 2
ID | Place | Price
-----------------------------
1 | Cali | 30
2 | Mexi | 10
Operation to perform:
Table1(Price) - Table2(Price) for ID = 1
New Table 1
ID | Name | Price
-----------------------------
1 | Casio | 0
2 | Titan | 40
ID matches in both tables
You should consider another database design to handle this case.
But to answer your question, you can create a view :
create view Differences2 as (
select t1.id, t1.price - t2.price
from t1, t2
where t1.id = t2.id
)
As you told both table will have same ID column you can use following query.
SELECT table1.ID, table1.Name, (table1.Price-table2.Price) AS Price
FROM table1
INNER JOIN table2 ON table1.ID = table2.ID
If you want to update record you can use following:
UPDATE table1
INNER JOIN table2 ON table1.ID = table2.ID
SET table1.Price = (table1.Price-table2.Price)

Mysql IN query instead of multiple AND conditon

I have three mysql tables, category,students and student_category. for each student there is 1 or more category will be there and it is stored in student_category as follows.
1) Categgory
----------------------------
id | category_name
---------------------------
1 | A
2 | B
3 | C
4 | D
2) Students
--------------------------
id | name
--------------------------
1 | John
2 | Kumar
3 | Ashok
4 | Jorge
5 | Suku
-------------------------
2) student_category
-----------------------------------------
id | student_id | category_id
-----------------------------------------
1 | 1 | 2
2 | 1 | 4
3 | 2 | 3
4 | 2 | 1
5 | 3 | 2
------------------------------------------
I need to select students which contain category_id 2 and 4.
i used query as follows but it return either students contain category 2 or category 4.
select A.name from students A, student_category B where A.id=B.student_id
and B.category_id IN (2,4)
Try this query:
SELECT t1.id,
t3.name
FROM students t1
INNER JOIN student_category t2
ON t1.id = t2.student_id
INNER JOIN students t3
ON t1.id = t3.id
WHERE t2.category_id IN (2, 4)
GROUP BY t1.id
HAVING COUNT(DISTINCT t2.category_id) = 2
Explanation:
This query joins together the students and student_category tables, and then removes all records which are not category 2 or 4. This means that each student would then only have category 2 and 4 records associated with him. The HAVING clause then restricts further by requiring that a student have two distinct categories, which if true must mean that the student has both category 2 and 4.
Demo here:
SQLFiddle
try this :
select name from Students where id in (select student_id from student_category where category_id in (2,4))
your query is fine btw.
Try this one:
select
s.name
from
Students s,
Categgory c,
student_category sc
where
sc.student_id = s.id
and sc.category_id = c.id
and c.id = 2
and c.id = 4
You can check it on SQL Fiddle.
Have to take distinct student name as it will repeat if a student falls in more than one category.

Categories