MySQL: Select several rows based on several keys from two different tables - php

I have these two tables - user_schedules and user_schedule_meta, shown below:
------------------------------------
| id | scheduler_id | status |
------------------------------------
1 3 pending
2 5 active
3 6 active
and
----------------------------------------------
| id | user_schedule_id | meta_key |meta_value
----------------------------------------------
1 3 course-id 135
2 3 session-id 15
3 3 schedule-id 120
I want to write a query to enable me select, for example, from both tables where EVERYONE of the below 5 conditions are met:
user_schedule_id = 3
scheduler_id = 6
session_id = 15
course-id = 135
schedule-id = 120
This is what I have so far, but it is not working:
SELECT user_schedule_meta.`id` FROM user_schedule_meta, user_schedules
WHERE user_schedules.`scheduler_id` = 6
AND user_schedules.id = user_schedule_meta.`user_schedule_id`
AND (
(user_schedule_meta.`meta_key` = 'course-id' AND user_schedule_meta.`meta_value` = 135)
OR (user_schedule_meta.`meta_key` = 'session-id' AND user_schedule_meta.`meta_value` = 15)
OR (user_schedule_meta.`meta_key` = 'daily-schedule-id' AND user_schedule_meta.`meta_value` = 120)
)
GROUP BY user_schedule_meta.`id`
Any suggestions what I am not doing right?

This is a typical key-value store lookup problem. These are trickier than they look in SQL, in that they require multiple JOIN operations.
You need a virtual table with one row per user_schedules.id value, then you can filter it. So
SELECT u.id, u.scheduler_id
FROM user_schedules u
JOIN user_schedule_meta a ON u.id=a.user_schedule_id AND a.meta_key='course-id'
JOIN user_schedule_meta b ON u.id=b.user_schedule_id AND b.meta_key='session-id'
JOIN user_schedule_meta c ON u.id=c.user_schedule_id AND c.meta_key='daily-schedule-id'
WHERE a.meta_value = 135 -- value associated with course-id
AND b.meta_value=15 -- value associated with session-id
AND c.meta_value=120 -- value associated with daily-schedule-id
Notice also that you can list your table with associated attributes like this. This trick of joining the key/value table multiple times is a kind of pivot operation. I use LEFT JOIN because it will allow the result set to show rows where an attribute is missing.
SELECT u.id, u.scheduler_id, u.status,
a.meta_value AS course_id,
b.meta_value AS session_id,
c.meta_value AS daily_schedule_id
FROM user_schedules u
LEFT JOIN user_schedule_meta a ON u.id=a.user_schedule_id AND a.meta_key='course-id'
LEFT JOIN user_schedule_meta b ON u.id=b.user_schedule_id AND b.meta_key='session-id'
LEFT JOIN user_schedule_meta c ON u.id=c.user_schedule_id AND c.meta_key='daily-schedule-id'

try this is code
select * from user_schedule_meta where user_schedule_id=3 and
(meta_key='session-id' AND meta_value=15
or meta_key='daily-schedule-id' AND meta_value=120
or meta_key='course-id' AND meta_value=135
)

Related

How can I filter one column by two AND conditions?

The question has been resolved. But if you have a "better" or another way to do it then feel free to add a comment! Thanks all for reading! :)
I'm trying to make a dynamic query. Everything is working perfectly except for one thing. I've Google'd for days but I can't figure out how I can make the following work;
SELECT project.name, project.description, track.name, track.description
, SDG.position, SDG.title, SDG.description
, sprint_numbers.number, sprint_options.option
, resources.name, resources.description
, URLs.URL
FROM project INNER JOIN track ON project.track_id = track.id
INNER JOIN project_SDG ON project.id = project_SDG.project_id
INNER JOIN SDG ON project_SDG.SDG_id = SDG.id
INNER JOIN sprint ON sprint.project_id = project.id
INNER JOIN sprint_numbers ON sprint_numbers.id = sprint.sprint_number_id
INNER JOIN sprint_options ON sprint_options.id = sprint.sprint_option_id
INNER JOIN resources ON project.id = resources.project_id
INNER JOIN URLs ON URLs.id = resources.id
WHERE 1=1
AND MATCH (project.name) AGAINST (:name_project)
AND MATCH (project.description) AGAINST (:description_project)
AND SDG.id = :SDG_1
AND SDG.id = :SDG_2
The query executes but does not return anything. The problem is that the SDG.id can't be true to both :SDG_1 and :SDG_2.
Using the OR operator works, but that does not return it the way I want. It must "act" as an AND operator. (:SDG_1 & :SDG_2 are the names of the PHP variables that bind to the SQL statement parameters.)
The query should filter for both values. The values given to :SDG_1 and :SDG_2 must both exist in the SDG.id column of the project_SDG table. If the value of :SDG_1 exists, but :SDG_2 not, then the query should not return anything.
I found this on StackOverflow but it did not work for me: SELECTING with multiple WHERE conditions on same column
I hope someone can help me out.
EDIT: minimal reproducible example
QUERY:
SELECT * FROM project
INNER JOIN project_SDG ON project.id = project_SDG.project_id
INNER JOIN SDG ON project_SDG.SDG_id = SDG.id
WHERE SDG.id = 1 AND SDG.id = 7 AND SDG.id = 14 AND SDG.id = 17
Project table
+------------------+---------------------------+------------+
| id name | description | track_id |
+------------------+---------------------------+------------+
| 1 project name | This is a description 2 | |
+------------------+---------------------------+------------+
SDG table
+-----+-----------+-------------+---------------------------------------------+
| id | position | title | description |
+-----+-----------+-------------+---------------------------------------------+
| 1 | 1 | SDG 1 to 17 | There're multiple SDGs ranging from 1 to 17 |
| 17 | 17 | SDG 1 to 17 | There're multiple SDGs ranging from 1 to 17 |
+-----+-----------+-------------+---------------------------------------------+
project.SDG (bridge-table)
+------------+--------+
| project.id | SDG.id |
+------------+--------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
+------------+--------+
You want for each project.id both values :SDG_1 and :SDG_2 to exist for SDG.id, so use this in the WHERE clause:
WHERE 1=1
AND MATCH (project.name) AGAINST (:name_project)
AND MATCH (project.description) AGAINST (:description_project)
AND project.id IN (
SELECT project_id
FROM project_SDG
WHERE SDG_id IN (:SDG_1, :SDG_2)
GROUP BY project_id
HAVING COUNT(DISTINCT SDG_id) = 2
)
Could you provide a minimal reproducible example for your query?
Generally speaking, one field cannot be equal to two different values in the same time. So, you have either mixed up the logical operators or you need two different fields.
I can assume that in your case there may be several related records with different values. In this case, you need to join the same table twice with different aliases. Let's say as SDG1 and SDG2. After that you can compare
... `SDG1`.id = :SDG_1 AND `SDG2`.id = :SDG_2
Update:
The win trick is groupping. You can enumerate all required SDG IDs and count how many of them is in group. Just for example in case of two IDs:
SELECT project.id
FROM project
JOIN project_SDG ON project_SDG.project_id = project.id
JOIN SDG ON SDG.id = project_SDG.SDG_id
WHERE SDG.id IN(1,2)
GROUP BY project.id
HAVING COUNT(*) = 2
See my sandbox here: https://www.db-fiddle.com/f/pixe3Zcs75Mq2PyCYPk913/0
If you need all project's fields, you have to put this into sub-query as
... WHERE id IN ( subquery here )
Subquery example: https://www.db-fiddle.com/f/pixe3Zcs75Mq2PyCYPk913/1
I have already answered here, but I have another approch.
1. Find bunch of IDs assotiated with some project
To find project IDs we can test lonely pivot table without any join:
SELECT project_id FROM project_SDG
WHERE SDG_id IN(1,2,6)
GROUP BY project_id HAVING COUNT(*) = 3
it gives us list of Project IDs
2. Access all project fields and add extra conditions
SELECT project.*
FROM project
JOIN (
SELECT project_id FROM project_SDG
WHERE SDG_id IN(1,2,6)
GROUP BY project_id HAVING COUNT(*) = 3
) AS ids ON ids.project_id = project.id
WHERE
MATCH(project.name) AGAINST ('project') AND
MATCH(project.description) AGAINST ('sit')
you can play with it here: https://www.db-fiddle.com/f/pixe3Zcs75Mq2PyCYPk913/3
3. Prepare query on the PHP side
I will use known technique to prepare SQL statement.
$ids = [1, 2, 6]; // it can come from request parameters
$text1 = 'project';
$text2 = 'sit';
// build ?,?,?,... pattern
$qmarks = implode(',', array_fill(0, count($ids), '?'));
// Use SQL query above
$sth = $dbh->prepare("
SELECT project.*
FROM project
JOIN (
SELECT project_id FROM project_SDG
WHERE SDG_id IN({$qmarks})
GROUP BY project_id HAVING COUNT(*) = ?
) AS ids ON ids.project_id = project.id
WHERE
MATCH(project.name) AGAINST (?) AND
MATCH(project.description) AGAINST (?)
");
$sth->execute(array_merge($ids, [count($ids), $text1, $text2]));
$records = $sth->fetchAll();

Fetch data from 4 tables mysql

I have 4 tables as below
Table1: restos
Structure:
resu_id resu_name resu_address
-------------------------------------
1 ABC Exapmple
2 DEF Example
3 GHD Example
Table2:foodtype
Structure:
id typename
---------------
12 Indian
23 Punjabi
Table3: resto_foodtypes
Structure:
resu_id foodty_id
--------------------
1 12
2 23
3 12
Table4: discnts
Structure:
id resu_id amt_dscPer(%age discount)
---------------------------
19 1 15
20 2 25
Now i want to display the restaurant along with discounts available for the restauarant.
Currently restaurants are getting displayed but for the restaurant not present in discnts table are returning null values from discnts table.
below is the query that m using
SELECT * from `restos` r join resto_foodtypes rf on rf.resu_id = r.resu_id
join foodtype f on rf.foodty_id = f.id left join discnts dcfm on
r.resu_id= dcfm.resu_id where true;
I want that the restaurants that are not present in discnts table should not be included in resultset. For e.g. resu_id=3 is not present in discnts table.
To exclude results that have no entry in discnts table, use a INNER JOIN instead of a LEFT JOIN.
To include these results, but have a 0 displayed (instead of the defaults NULL), you can use IFNULL(expr, 0) function:
SELECT
r.resu_name AS Name,
f.typename AS Food,
IFNULL(dcfm.amt_dscPer, 0) AS Discount
FROM `restos` r
JOIN resto_foodtypes rf ON rf.resu_id = r.resu_id
JOIN foodtype f ON rf.foodty_id = f.id
LEFT JOIN discnts dcfm ON r.resu_id= dcfm.resu_id;
IFNULL returns the first parameter if it is not null, the second if expr is indeed null.

MySql Select Result Having Combined Multiple Queries From Both the Same Table and Others

I have a table ('names') which includes data related with other data in other tables relying on ids. For example:
*Names table
id | name | predecessor | successor | house | birthplace
-----------------------------------------------------------------
10 Bayezid II 9 11 4 NULL
11 Selim I 10 12 4 5
12 Suleiman 11 13 4 61
*Houses table
id | house
--------------
4 House of Osman
*Places table
id | place
--------------
5 Amasya
61 Trabzon
What I'm trying to accomplish is to construct a query which results in returning whole information depending on the id, like:
{"result":[{
"id":"11",
"name":"Selim I",
"predecessor": "Bayezid II",
"successor": "Suleiman",
"house":"House of Osman",
"birthplace":"Amasya"
}]}
So, the name of the house and the birthplace are brought from other tables ('houses', 'places') whereas the predecessor and the successor are from the same table. I need help constructing this query. Thank you.
Just self-join a couple times, once to get the predecessor's row (aliased n0 below), and once more for the successor's (n2):
SELECT n1.id, n1.name, n0.name AS predecessor, n2.name AS successor
FROM names n1
LEFT JOIN names n0 ON n1.predecessor = n0.id
LEFT JOIN names n2 ON n1.successor = n2.id
SQL Fiddle demo
Joining to get the house and birthplace are left as an exercise for the reader.
Try this:
select n.id,
n.name,
n1.name as predecessor,
n2.name as successor,
h.house,
p.place
from names n
inner join names n1 on n.id = n1.predecessor
inner join names n2 on n.id = n2.successor
left join Houses h on n.house = h.id
left join Place p on n.birthplace = p.id
where n.id = 11

PHP MySQL Highscore table

Im joining 3 tables to present a table with users highest score
My tables
game_log:
---ID---user_ID---score---time---
| 1 52 567 10 |
| 2 53 641 13 |
| 3 52 465 8 |
| 4 53 451 14 |
---------------------------------
users:
---ID---name---countyid---
| 52 Dave 1 |
| 53 John 2 |
------------------------
county:
---countyid---countyname---
| 1 Orange wichit |
| 2 Orange clemts |
--------------------------
SQL:
SELECT * FROM game_log
INNER JOIN users ON game_log.user_ID=users.ID
INNER JOIN county ON users.countyid=county.countyid
ORDER BY game_log.score DESC , game_log.time LIMIT 20";
Above code gives me this result:
Rank---Name--------County------Score---Time
1 John Orange clemts 641 13
2 Dave Orange wichit 567 10
3 John Orange clemts 465 8
4 Dave Orange wichit 451 14
My problem is that I want the highscore table to display the top 20 users with the highest score, not the 20 highest scores.
Like this:
Rank---Name--------County------Score---Time
1 John Orange clemts 641 13
2 Dave Orange wichit 567 10
Need som help with this, not familiar with joining tables ;-)
This approach will show the top 20 users and each user's highest score, and if they have multiple instances of the same score, it'll show the information for the earliest one (lowest time value for that user and score).
SELECT *
FROM game_log gl
INNER JOIN users u
ON gl.user_ID = u.ID
INNER JOIN county c
ON u.countyid = c.countyid
WHERE not exists (select 1
from game_log gl2
where gl2.user_id = gl.user_id
and gl2.score > gl.score)
and not exists (select 1
from game_log gl2
where gl2.user_id = gl.user_id
and gl2.time < gl.time
and gl2.score = gl.score)
ORDER BY gl.score DESC, gl.time LIMIT 20;
Without doing this, if the same user in the top 20 had the same score 2+ times, they would be listed 2+ times, and you would not get back 20 people by using LIMIT 20 because the same person would be taking up N rows out of that 20.
SQL Fiddle here showing data with a tie: http://sqlfiddle.com/#!2/0ac931/5/0
GROUP BY should do the job.
SELECT users.ID, users.name, county.countyname, MAX(game_log.score) AS score, game_log.time
FROM game_log
INNER JOIN users ON game_log.user_ID = users.ID
INNER JOIN county ON users.countyid = county.countyid
GROUP BY game_log.user_ID
ORDER BY game_log.score DESC, game_log.time
LIMIT 20;
Try it out with SQL Fiddle.
I would do this with the not exists approach to get the highest score for each user. The rest of the query is the same:
SELECT *
FROM game_log gl INNER JOIN
users u
ON gl.user_ID = u.ID INNER JOIN
county c
ON u.countyid = c.countyid
WHERE not exists (select 1
from game_log gl2
where gl2.user_id = gl.user_id and gl2.score > gl.score
)
ORDER BY gl.score DESC, gl.time
LIMIT 20;
The where clause is saying "keep this row if no other row for the same user has a higher score".
Another way to do this is with the aggregation approach:
SELECT *
FROM (select user_id, max(score) as maxscore
from game_log gl
group by user_id
) gl INNER JOIN
users u
ON gl.user_ID = u.ID INNER JOIN
county c
ON u.countyid = c.countyid
ORDER BY gl.maxscore DESC
LIMIT 20;
But this method loses the information about time. It is possible to include that, but it makes the query more complicated.

how to perform select from multiple tables by several ids

i have 3 tables that looks like this:
game_table
+---------+------------+------------+----------------------+----------+
| game_id | game_title | sponser_id | game expiration date | prize_id |
+---------+------------+------------+----------------------+----------+
prize_table
+----------+---------------------------+------------+-------------+--------------------+--------------------------------------------+
| prize_id | prize_image_name | prize_cost | prize_title | remaining_quantity | prize_description |
+----------+---------------------------+------------+-------------+--------------------+--------------------------------------------+
sponser_table
+------------+--------------+
| sponser_id | sponser_name |
+------------+--------------+
how do i build query that select all data from the 3 tables that
meat the statement that go's something like pseudo code:
select all data from game_table and prize_table and sponser_table where game_table.sponser_id = 2 and game_table.prize_id = 2
i tried something like this :
SELECT game_list.*, prize_list.* ,sponser_list.* FROM game_list, prize_list,sponser_list
WHERE game_list.sponser_id=2 And game_list.prize_id = 2 And game_list.game_id=2 ;
but it gave me no good results .
You had a WHERE clause to limit to the correct ids, but you had no join conditions to relate your tables. Instead of the implicit join syntax you attempted (comma-separated table list), use a explicit JOINs with stated relating columns:
SELECT
game_list.*,
prize_list.* ,
sponser_list.*
FROM
game_list
JOIN prize_list ON game_list.prize_id = prize_list.prize_id
JOIN sponser_list ON game_list.sponser_id = sponser_list.sponser_id
WHERE game_list.sponser_id=2 And game_list.prize_id = 2 And game_list.game_id=2 ;
I would recommend against selecting all columns from each table though, since you are duplicating the id columns in at least two places. Instead, be explicit about the columns you want. This will also help you if you later add additional columns to these tables that should not be included in this query.
SELECT
game_id,
game_title,
game_list.sponser_id,
game_expiration_date,
game_list.prize_id,
prize_image_name,
prize_cost,
prize_title,
remaining_quantity,
prize_description,
sponser_name
FROM
game_list
JOIN prize_list ON game_list.prize_id = prize_list.prize_id
JOIN sponser_list ON game_list.sponser_id = sponser_list.sponser_id
WHERE game_list.sponser_id=2 And game_list.prize_id = 2 And game_list.game_id=2 ;
SELECT *
FROM game_table
JOIN prize_table USING (prize_id)
JOIN sponser_table USING (sponser_id)
WHERE sponser_id = 2
AND prize_id = 2
AND game_id = 2
SELECT
game_list.*, prize_list.* ,sponser_list.*
FROM game_list
JOIN prize_list ON game_list.prize_id = prize_list.prize_id
JOIN sponser_list ON game_list.sponser_id = sponser_list.sponser_id
WHERE
game_list.sponser_id=2 And game_list.prize_id = 2 And game_list.game_id=2 ;
From your description it appears that the tables may be related. If they are, you need to use a join, like this:
SELECT *
FROM game_table g
LEFT OUTER JOIN prize_table p ON p.prize_id=g.prize_id
LEFT OUTER JOIN sponser_table s ON s.sponser_id=g.sponser_id
WHERE g.game_id=2

Categories