Hello my issue is I do not know how to use select multiple times in a query when searching through a data base. Here is what I have below
$statement = $db->query(" SELECT M.name
FROM actors A, roles R, movies M
WHERE M.id = R.movie_id AND R.actor_id = A.id AND A.first_name = '$fname' AND A.last_name = '$lname';");
Right now this statement works, it is fine. but for another part of my project I would like for it to look like
SELECT ...
FROM ...
WHERE ... = (SELECT ...
FROM ...
WHERE...)
I know that looks bad, but I am a beginner in SQL, and I can't seem to code this without errors. Any tips would be great! If any other information is needed just ask :)
Thank you very much
You can use such constructs as
SELECT something
FROM sometable
WHERE column = (SELECT a FROM anothertable WHERE b = constant)
or
SELECT something
FROM sometable
WHERE column IN (SELECT value FROM anothertable WHERE c = constant)
In the first case column = the second, inner, SELECT query must return precisely one value.
In the second case column IN the inner query may return a bunch of values in one column. It can return no values at all.
Related
Here is my query which is taking 17.9397 sec time to get response:
SELECT allbar.iBarID AS iBarID,
allbar.vName AS vName,
allbar.tAddress AS tAddress,
allbar.tDescription AS tDescription,
(SELECT COUNT(*)
FROM tbl_post p
WHERE p.vBarIDs = allbar.iBarID) AS `total_post`,
allbar.bar_usbg AS bar_usbg,
allbar.bar_enhance AS bar_enhance,
(SELECT count(*)
FROM tbl_user
WHERE FIND_IN_SET(allbar.iBarID,vBarIDs)
AND (eType = 'Bartender'
OR eType = 'Bar Manager'
OR eType = 'Bar Owner')) AS countAss,
allbar.eStatus AS eStatus
FROM
(SELECT DISTINCT b.iBarID AS iBarID,
b.vName AS vName,
b.tAddress AS tAddress,
(CASE LENGTH(b.tDescription) WHEN 0 THEN '' WHEN LENGTH(b.tDescription) > 0
AND LENGTH(b.tDescription) < 50 THEN CONCAT(LEFT(b.tDescription, 50),'...') ELSE b.tDescription END) AS tDescription,
b.usbg AS bar_usbg,
b.enhance AS bar_enhance,
b.eStatus AS eStatus
FROM tbl_bar b,
tbl_user u
WHERE b.iBarID <> '-10') AS allbar
I have tried EXPLAIN, here is the result of that:
Can anyone explain me this EXPLAIN result?
You should totaly rewrite that query, it's complete nonsense.
In this part
(SELECT DISTINCT b.<whatever>
FROM tbl_bar b,
tbl_user u
WHERE b.iBarID <> '-10') AS allbar
what you're basically doing is connecting every row from table tbl_bar with every row from tbl_user. Then filter tbl_bar, and when everything is selected (maybe MySQL has to write everything in a temporary table before doing this) return the result set without duplicates. You don't ever want to do that. Especially when you don't even select anything from tbl_user. When there's a connection, specify it. If there's none, don't join those tables or create a connection. I don't know if or how your tables are connected, but it should look something like this:
(SELECT DISTINCT b.<whatever>
FROM tbl_bar b
JOIN tbl_user u ON b.user_id = u.id /*or whatever the connection is*/
WHERE b.iBarID <> '-10') AS allbar
Then you have this ugly subquery.
(SELECT COUNT(*)
FROM tbl_post p
WHERE p.vBarIDs = allbar.iBarID) AS `total_post`,
allbar.bar_usbg AS bar_usbg,
allbar.bar_enhance AS bar_enhance,
which is by the way dependent (see your explain output). Which means, that this subquery is executed for every row of your outer query (yes, the one with the cross join as discussed above). Instead of this subquery, join the table in the outer query and work with GROUP BY.
So far the query should look something like this:
SELECT
b.iBarID AS iBarID,
b.vName AS vName,
b.tAddress AS tAddress,
b.tDescription AS tDescription,
COUNT(*) AS `total_post`,
allbar.bar_usbg AS bar_usbg,
allbar.bar_enhance AS bar_enhance
FROM
tbl_bar b
JOIN tbl_user u ON b.user_id = u.id
JOIN tbl_post p ON p.vBarIDs = b.iBarID
WHERE b.iBarID <> '-10'
GROUP BY b.iBarID
(In fact, this is not really right. Rule is, every column in the SELECT clause should either be in the GROUP BY clause as well or have an aggregate function (like count() or max() applied to it. Otherwise a random row of each group is displayed. But this is just an example. You will have to work out the details.)
Now comes the worst part.
(SELECT count(*)
FROM tbl_user
WHERE FIND_IN_SET(allbar.iBarID,vBarIDs)
AND (eType = 'Bartender'
OR eType = 'Bar Manager'
OR eType = 'Bar Owner')) AS countAss,
allbar.eStatus AS eStatus
The use of FIND_IN_SET() suggests, that you're storing multiple values in one column. Again, you never ever want to do that. Please read this answer to Is storing a delimited list in a database column really that bad? and then redesign your database. I won't help you with this one, as this clearly is stuff for a separate question.
All this didn't really explain the EXPLAIN result. For this question, I would have to write a whole tutorial, which I won't do, since everything is in the manual, as always.
I have a first SQL query getting a table with id_member as parameter
$stmt = $mysqli -> prepare("SELECT a.id_alerte,
a.nom_alerte,
ar.id_roster,
r.nom_roster,
FROM alerte a,
alerte_par_roster ar,
roster_par_membre rm
INNER JOIN roster r
ON r.id_roster = rm.id_roster
WHERE rm.id_roster = ar.id_roster
AND ar.id_alerte = a.id_alerte
AND rm.id_membre = ?");
$stmt->bind_param('i', $id_membre);
I need to insert a second query counting the number of lines in another table.
The second query is:
"SELECT COUNT(DISTINCT id_roster)
FROM disponibilites_par_member_alertes
WHERE id_member = ?
AND id_alerte = ?"
As you can notice id_member is the identical in both queries but id_alerte (used as a parameter of the second query is a result of a first query).
I hope I am clear.
Any idea will be very welcome
I don't fully understand what your queries are intended to do. It would help to have clear information on the table structure and what each of the fields represents. I've tried to make a guess based on the table names but can't be certain it's correct.
The first thing I did was just to transform your WHERE condition such that each of the tables are joined explicitly. This is just for readability.
SELECT
a.id_alerte,
a.nom_alerte,
ar.id_roster,
r.nom_roster
FROM alerte a
INNER JOIN alerte_par_roster ar
ON ar.id_alerte = a.id_alerte
INNER JOIN roster_par_membre rm
ON rm.id_roster = ar.id_roster
INNER JOIN roster r
ON r.id_roster = rm.id_roster
WHERE
rm.id_membre = ?
Now we combine with the other query:
SELECT
a.id_alerte,
a.nom_alerte,
ar.id_roster,
r.nom_roster,
rc.id_roster_count
FROM alerte a
INNER JOIN alerte_par_roster ar
ON ar.id_alerte = a.id_alerte
INNER JOIN roster_par_membre rm
ON rm.id_roster = ar.id_roster
INNER JOIN roster r
ON r.id_roster = rm.id_roster
LEFT JOIN (
SELECT id_membre, id_alerte, COUNT(DISTINCT id_roster) AS id_roster_count
FROM disponibilites_par_member_alertes
GROUP BY id_membre, id_alerte
) AS rc
ON rc.id_membre = rm.id_membre
AND rc.id_alerte = ar.id_alerte
WHERE
rm.id_membre = ?
With my own generated data I get results like this:
If this is not enough to solve the problem you will need to provide more details about the tables and the design.
I have a bit of a situation here.
I have a query:
SELECT DISTINCT (testrecurring.id), testrecurring.cxl, testci.cd
FROM testci, testrecurring
WHERE (testci.id = testrecurring.id)
AND testci.x_origin='1'
ORDER BY testrecurring.id DESC;
Now, if a var is not set, I need to do a select on this query, and here is the catch. I need to exclude some id's. Here is how I'm doing it now.
I create a table with that query: create table xxx SELECT * ..... and now the results from my previous query are inside another table called xxx.
Then:
if (!isset($var)) {
$delete = mysql_query("delete from xxx USING xxx, future_recurring where xxx.id = future_recurring.id");
}
and after the records have been deleted I do my final select * from xxx.
This works just fine, the only thing is that I need to redo all this logic by not creating any tables. Maybe doing some joins, I'm not sure how to proceed.
I hope this is not very confusing.
Any ideas?
And now how about this?:
SELECT tr.id, tr.cxl, tci.cd
FROM testci AS tci
INNER JOIN testrecurring AS tr
ON tci.id = tr.id
LEFT OUTER JOIN future_recurring AS fr
ON tr.id = fr.id
WHERE tci.x_origin='1'
AND fr.id IS NULL
GROUP BY tr.id, tr.cxl, tci.cd
ORDER BY tr.id DESC
This only includes results in which the testrecurring.id is NOT FOUND in future_recurring
You just need to add a where condition to exclude the rows you don't want:
SELECT *
FROM testci
JOIN testrecurring on testrecurring.id = testci.id
WHERE testci.x_origin='1'
AND testci.id NOT IN (SELECT id FROM future_recurring)
ORDER BY testrecurring.id DESC;
Or you could try this which might give better performance:
SELECT *
FROM testci
JOIN testrecurring on testrecurring.id = testci.id
LEFT JOIN future_recurring on future_recurring.id = testci.id
WHERE testci.x_origin='1'
AND future_recurring id IS null
ORDER BY testrecurring.id DESC;
Although, if your indexes are good and sane and your data sets aren't enormous then the performance should be close.
Here is my code but im sure its not the correct way of doing this.
mysql_query("SELECT * FROM BlockUsers
WHERE OwnerID =
(SELECT ID FROM UserAccounts WHERE Code='$UserCode')
AND
BlockID =
(SELECT ID FROM UserAccounts WHERE Code='$BlockUserCode')
LIMIT 1", $DB)
Can someone help? thanks!
Yes, but when you're doing an equality test like that (=, <, >, etc...), the subquery has to return a single value. Otherwise it'd be somevalue = [list of result rows], which makes no sense.
You'd want:
SELECT * FROM BlockUsers
WHERE OwnerID IN (SELECT ID FROM UserAccounts WHERE.....)
^^--- use 'in' instead of '=';
SELECT * FROM BlockUsers
WHERE OwnerID IN
(SELECT ID FROM UserAccounts WHERE Code='$UserCode')
AND
BlockID IN
(SELECT ID FROM UserAccounts WHERE Code='$BlockUserCode')
LIMIT 1
or
SELECT * FROM BlockUsers AS bu
INNER JOIN UserAccounts AS ua1 ON ua1.ID = bu.OwnerID
INNER JOIN UserAccounts AS ua2 ON ua2.ID = bu.BlockID
WHERE ua1.Code = '$UserCode' AND ua2.Code = '$BlockUserCode'
LIMIT 1
I think. I didn't test any of this, but I'm pretty sure it's close.
Edit:
I just noticed you're using MySQL. You definitely want to do inner joins instead of sub selects. In MySQL those sub selects will create derived tables which have no indexes. Looking for OwnerID and BlockID in those derived tables will do a full table scan of them. This may not matter if $UserCode and $BlockUserCode will narrow the results of the sub selects down to a single row, but if they return quite a few rows it will really slow your query down.
Instead of using subqueries, you can just JOIN UserAccounts to get the rows you want.
SELECT BlockUsers.* FROM BlockUsers
JOIN UserAccounts as UA1 ON Code='$UserCode' AND OwnerID = UA1.ID
JOIN UserAccounts as UA2 ON Code='$BlockUserCode' AND BlockID = UA2.ID
LIMIT 1
Yes you can, Subqueries and you can find the official mysql reference here: http://dev.mysql.com/doc/refman/5.0/en/subqueries.html
and from your query i think you are good to go assuming your subqueries only return 1 value, or as #Marc B points use IN instead of the equal sign.
Try to use
SELECT * FROM BlockUsers, UserAccounts
WHERE (OwnerID = ID and BlockID=ID and Code in ('$BlockUserCode', '$UserCode')
LIMIT 1
Im trying to construct a query that goes over 3 tables and im COMPLETELY stumped ... my knowledge limit is basic 1 table query and i need some help before i stick my head in a blender.
I have the following query
SELECT * FROM internalrole WHERE introle = $imarole
Im fine with that part .. its the next thats getting me all stressed.
That query returns the following columns ( id, user_id, introle, proven, used )
What i then need to do is take the user_id from the results returned and use it to get the following
SELECT * FROM users WHERE id = user_id(from previous query) AND archive = 0 and status = 8
I need to put that into 1 query, but wait, theres more .... from the results there, i need to check if that user's 'id' is in the availability table, if it is, check the date ( column name is date ) and if it matches todays date, dont return that one user.
I need to put all that in one query :S ... i have NO IDEA how to do it, thinking about it makes my head shake ... If someone could help me out, i would be eternaly grateful.
Cheers,
Use INNER JOIN, which links tables to each other based on a common attribute (typically a primary - foreign key relationship)
say an attribute, 'id', links table1 and table2
SELECT t1.att1, t2.att2
FROM table1 t1
INNER JOIN table2 t2
ON t1.id = t2.id --essentially, this links ids that are equal with each other together to make one large table row
To add more tables, just add more join clauses.
SELECT u.*
FROM internalrole ir
INNER JOIN users u
ON ir.user_id = u.id
AND u.archive = 0
AND u.status = 8
LEFT JOIN availability a
ON ir.user_id = a.user_id
AND a.date = CURDATE()
WHERE ir.introle = $imarole
AND a.user_id IS NULL /* User does NOT exist in availability table w/ today's date */
EDIT: This second query is based on the comments below, asking to show only users who do exist in the availability table.
SELECT u.*
FROM internalrole ir
INNER JOIN users u
ON ir.user_id = u.id
AND u.archive = 0
AND u.status = 8
INNER JOIN availability a
ON ir.user_id = a.user_id
WHERE ir.introle = $imarole
Hmm, maybe something like this
SELECT * FROM users WHERE id IN (SELECT user_id FROM internalrole WHERE introle = $imarole) AND archive = 0 and status = 8;
A handy thing for me to remember is that tables are essentially arrays in SQL.
HTH!
Nested queries are your friend.
SELECT * FROM users WHERE id in (SELECT user_id FROM internalrole WHERE introle = $imarole) AND archive = 0 and status = 8
Alternatively joins:
SELECT * FROM users INNER JOIN internalrole ON users.id = internalrole.user_id WHERE internalrole.user_id = $imarole AND users.archive = 0 and users.status = 8