Dynamic Table Joining Based on Another Table Column Value? - mysql

I'm trying to figure out if there is a simple way to dynamically load a 2nd table based on the column value of the first table with mysql
Servers (Table 1):
ID | Game | Title
Servers_1 (Table 2, option 1):
server_id (links to servers.id) | game_version | players | plugins
Servers_2 (Table 2, option 2):
server_id (links to servers.id) | game_version | players | mods | game_map
Servers_etc. (Table 2, option etc.)
Trying to figure out how to do something like
left_join servers_[servers.game] on servers.id = servers_[servers.game].server_id
So it would grab the value of servers.game and use that to finish the table name. If this is not possible, then is a case statement possible such as:
Left_Join
if ( servers.game == 1 ) 'servers_1'
elseif ( servers.game == 2 ) 'servers_2'
elseif ( servers.game == 3 ) 'servers_3'

One option would be to LEFT JOIN each of the tables and use a CASE statement to return the appropriate data.
Something like this should help get you started:
SELECT S.Id, S.Game, S.Title,
CASE S.Game
WHEN 1 THEN S1.game_version
WHEN 2 THEN S2.game_version
END game_version,
...
FROM Servers S
LEFT JOIN Servers_1 S1 ON S.id = S1.Server_Id AND S.Game = 1
LEFT JOIN Servers_2 S2 ON S.id = S2.Server_Id AND S.Game = 2
Instead of using CASE, you could probably just use COALESCE as each Id/Game should be unique and only 1 wouldn't be NULL:
SELECT COALESCE(S1.game_version,S2.game_version,...) game_version
If there is no way the same server id can be in multiple tables, then you can leave the AND S.Game... out of the LEFT JOINs as it wouldn't longer be needed. Depends on your unique keys.
Alternatively, you could use Dynamic SQL.

Related

How do I write code in SQL for following example

I have two tables, BOQ and DPR
Table : BOQ
ID | Buidling| Activity |Plan_Start_Date|Actual_Start_Date
1 |A-1 |Shuttering|02-02-2019 |15-02-2019
2 |A-2 |Shuttering|13-02-2019 |13-02-2019
Table : DPR
ID | Buidling| Activity |Date |
1 |A-1 |Shuttering|15-02-2019 |
I need to write an expression for column Actual_Start_Date such that when Building and Activity columns are matched in both tables then it should display the date in DPR table as Actual_Start_Date (in above case 15-02-2019 is Actual_Start_Date of id 1 in BOQ as the table and activity columns are matched).
If there is no matching value then Actual_Start_Date should show Plan_start_Date as in ID 2 of BOQ
You want a left join:
select . . ., -- whatever other columns you want
coalesce(dpr.date, boq.actual_start_date) as actual_start_date
from boq left join
dpr
on boq.building = dpr.building and
boq.activity = dpr.activity;
You can easily turn this into an update:
update boq join
dpr
on boq.building = dpr.building and
boq.activity = dpr.activity
set boq.actual_start_date = coalesce(dpr.date, boq.actual_start_date) ;

SQL Joins - Gather all from RIGHT table

I have two tables:
user-data:
id | userID | keyID | val
1 99 1 1
user-data-keys
id | key
1 is-staff
2 description
3 image
Now, when I run the following SQL, I get the desired output:
SELECT `key`,`val` FROM `user-data` RIGHT JOIN `user-data-keys` ON `user-data`.`keyID` = `user-data-keys`.`id`;
Which produces:
key | val
is-staff 1
description NULL
image NULL
Which is exactly what I want. However when I add a WHERE clause to the SQL:
SELECT `key`,`val` FROM `user-data` RIGHT JOIN `user-data-keys` ON `user-data`.`keyID` = `user-data-keys`.`id` WHERE `userID` = 99;
I only get the one row with is-staff in it. Which I understand, as I asked for only rows with userID = 99. However I am planning on storing lots of different user's information in the one user-data table, and I want to know if they have a NULL value for each of the keys. So how can I achieve this? I know it's got to be some kind of fancy join that I am not aware of.
So to clarify: i need the output like this:
key | val
is-staff 1
description NULL
image NULL
When using a WHERE userID = 99. Currently I only get one row whilst using a WHERE clause.
Move the predicate from the WHERE clause to the join:
SELECT `key`,`val` FROM `user-data` RIGHT JOIN `user-data-keys` ON `user-data`.`keyID` = `user-data-keys`.`id` and `userID` = 99;
If you want to get rows which are not matched then you can add where conditions with join like " ON user-data.keyID = user-data-keys.id AND userID = 99 " instead of " WHERE userID = 99 ",
SELECT `key`,`val`
FROM `user-data`
RIGHT JOIN `user-data-keys` ON `user-data`.`keyID` = `user-data-keys`.`id` AND `userID` = 99
WHERE 1;

Mysql SELECT where 2 column match set of values

I am doing product filter the point is the more specific the user select the products the less results should appear. At he moment I am writing multiple queries and storing in arrays and checking for array intersect, but the result is opposite, which means when user apply more filters, i will show more products.
So i am thinking there could be a SQL command which I don't know!
simplified example:
------------
table "filter"
------------
product
Spec
value
------------
Sample data
------------
book1,page,200
book1,cover,leather
book1,language,en
book2,page,300
book2,cover,paper
book2,language,de
book3,page,150
book3,cover,hard
book3,language,en
SELECT `product` FROM `filter` where ...
how do I select (page=200 and langauge=en)?
If understand correctly you are probably looking for something like this
SELECT product
FROM filter
WHERE (spec = 'page' AND value = '200')
OR (spec = 'language' AND value = 'en')
GROUP BY product
HAVING COUNT(*) = 2 -- 2 here represents number of spec-value pairs
Output:
| PRODUCT |
-----------
| book1 |
SQLFiddle
Another alternative, but less elegant. I just wanted to show another way of doing it.
SELECT DISTINCT product
FROM filter f
WHERE
EXISTS (SELECT 1 FROM filter WHERE spec = 'language' AND value = 'en' AND product = f.product)
AND EXISTS (SELECT 1 FROM filter WHERE spec = 'page' AND value = 200 AND product = f.product);

How to use result of an subquery multiple times into an query

A MySQL query needs the results of a subquery in different places, like this:
SELECT COUNT(*),(SELECT hash FROM sets WHERE ID=1)
FROM sets
WHERE hash=(SELECT hash FROM sets WHERE ID=1)
and XD=2;
Is there a way to avoid the double execution of the subquery (SELECT hash FROM sets WHERE ID=1)?
The result of the subquery always returns an valid hash value.
It is important that the result of the main query also includes the HASH.
First I tried a JOIN like this:
SELECT COUNT(*), m.hash FROM sets s INNER JOIN sets AS m
WHERE s.hash=m.hash AND id=1 AND xd=2;
If XD=2 doesn't match a row, the result is:
+----------+------+
| count(*) | HASH |
+----------+------+
| 0 | NULL |
+----------+------+
Instead of something like (what I need):
+----------+------+
| count(*) | HASH |
+----------+------+
| 0 | 8115e|
+----------+------+
Any ideas? Please let me know! Thank you in advance for any help.
//Edit:
finally that query only has to count all the entries in an table which has the same hash value like the entry with ID=1 and where XD=2. If no rows matches that (this case happend if XD is set to an other number), so return 0 and simply hash value.
SELECT SUM(xd = 2), hash
FROM sets
WHERE id = 1
If id is a PRIMARY KEY (which I assume it is since your are using a single-record query against it), then you can just drop the SUM:
SELECT xd = 2 AS cnt, hash
FROM sets
WHERE id = 1
Update:
Sorry, got your task wrong.
Try this:
SELECT si.hash, COUNT(so.hash)
FROM sets si
LEFT JOIN
sets so
ON so.hash = si.hash
AND so.xd = 2
WHERE si.id = 1
I normally nest the statements like the following
SELECT Count(ResultA.Hash2) AS Hash2Count,
ResultA.Hash1
FROM (SELECT S.Hash AS Hash2,
(SELECT s2.hash
FROM sets AS s2
WHERE s2.ID = 1) AS Hash1
FROM sets AS S
WHERE S.XD = 2) AS ResultA
WHERE ResultA.Hash2 = ResultA.Hash1
GROUP BY ResultA.Hash1
(this one is hand typed and not tested but you should get the point)
Hash1 is your subquery, once its nested, you can reference it by its alias in the outer query. It makes the query a little larger but I don't see that as a biggy.
If I understand correctly what you are trying to get, query should look like this:
select count(case xd when 2 then 1 else null end case), hash from sets where id = 1 group by hash
I agree with the other answers, that the GROUP BY may be better, but to answer the question as posed, here's how to eliminate the repetition:
SELECT COUNT(*), h.hash
FROM sets, (SELECT hash FROM sets WHERE ID=1) h
WHERE sets.hash=h.hash
and sets.ID=1 and sets.XD=2;

Change results in mysql query

I would like to manipulate the result I get from a query.
I have a set of 2.5m rows and there are 10 different ID's for a status. These statusses are not mapped in another table but I would like to manipulate the result I get in SQLyog.
What I would like to do is:
Count(Id) | Status
------------------
500.000 | 1
750.000 | 2
convert into a result
Count(Id) | Status
-------------------
500.000 | Initial order
750.000 | Cancelled
Can this be done in the query? Note that I'm not using PHP or a browser to display the results.
select
count(*) as TotalRecs,
case status
when 1 then "Initial Order"
when 2 then "Cancelled "
when 3 then "whatever "
else "all others "
end case as WordStatus
from
YourTable
group by
2
You can either inline it in a case statement
select COUNT(id),
case status
when 1 then 'initial order'
when 2 then 'cancelled'
# without an else, the rest go to NULL
end status
from tbl
group by status # yes, just on status
Or I would strongly encourage you to create a reference table for this
Tbl Status contains 2 columns ID and Description
select COUNT(tbl.id), status.description
from tbl
LEFT join status on status.id = tbl.status
group by status.description