i have 2 tables, one is called Classes which contains the ClassName and one is called FullSchedule which contains info including ClassName, DayVal and TimeVal.
What i want to do is, i want to select the classes that are not being using in a particular day and time, and this is my query :
SELECT `Classes`.`ClassName`
FROM `FullSchedule`
RIGHT OUTER JOIN `Classes`
ON `FullSchedule`.`ClassName` = `Classes`.`ClassName`
WHERE `FullSchedule`.`DayVal` = '$day' AND `FullSchedule`.`TimeVal` = `$time`
the result is the classes that are bing used(the common ones) though im using RIGHT OUTER JOIN and even if i change it to :
ON `FullSchedule`.`ClassName` != `Classes`.`ClassName`
it shows me all classes, no matter how i change it, it either get me all of the classes or just the classes that are being used. how to i get the classes that are not being used?
Try this:
SELECT `Classes`.`ClassName`
FROM `Classes`
WHERE Classes`.`ClassName` not in (
SELECT `FullSchedule`.`ClassName`
FROM `FullSchedule`
WHERE `FullSchedule`.`DayVal` = '$day' AND `FullSchedule`.`TimeVal` = `$time` )
At first retrieve data with WHERE conditions from table FullSchedule, then RIGHT JOIN it to Classes :
SELECT
c.`ClassName`
FROM (
SELECT
`ClassName`
FROM
`FullSchedule`
WHERE
`DayVal` = '$day' AND `TimeVal` = '$time'
) f RIGHT OUTER JOIN `Classes` c
ON f.`ClassName` != c.`ClassName`
Alternative to the JOIN option:
SELECT c.ClassName
FROM Classes c
WHERE NOT EXISTS ( SELECT *
FROM FullSchedule fs
WHERE fs.DayVal = $day
AND fs.TimeVal = $time
AND fs.ClassName = c.ClassName )
I suggest you put all three solutions in your analyzer and compare the execution plans to find out which is the best fit for your environment.
If the RIGHT OUTER JOIN syntax is absolutely necessary:
SELECT c.ClassName
FROM FullSchedule fs
RIGHT OUTER JOIN Classes c
ON fs.ClassName = c.ClassName
AND fs.DayVal = $day
AND fs.TimeVal = $time
WHERE fs.ClassName IS NULL
The RIGHT OUTER JOIN ensures the Classes table returns all of its rows and then matches FullSchedule rows on ClassName and those with DayVal and TimeVal values equal to $day and $time respectively.
The WHERE clause eliminates the rows where the FullSchedule table (with the ClassName, DayVal, and TimeVal restrictions) does not have a matching record.
SELECT c.ClassName
FROM classes c
LEFT
JOIN FullSchedule s
ON s.ClassName = c.ClassName
AND s.DayVal = '$day'
AND s.TimeVal = '$time' -- <-- NOT BACKTICKS
WHERE s.ClassName IS NULL;
Consider storing dates and times as a single entity. Much, much better idea.
Related
This is my query. Using WHERE IN works normally in the MySQL GUI, however, when I run this query, it returns an empty result. I have checked them manually if the dates exist in the events table, and they really do.
SELECT * FROM venues v
LEFT JOIN reservations r ON v.venue_id = r.venue_id
LEFT JOIN events e ON e.reservation_id = r.reservation_id
WHERE e.date_of_use NOT IN ('$dates')
$dates is just a string of dates (i.e., 11-11-2018, 11-12-2018).
There is an error in the $dates string.
It should be formatted as '11-11-2018', '11-12-2018' i.e. each value should be enclosed in quotes to make IN query work.
Currently, your query looks like
WHERE e.date_of_use NOT IN ('11-11-2018, 11-12-2018');
which will search for rows with 2 dates instead of single dates.
Here's how a correct query would look like,
SELECT * FROM venues v
JOIN reservations r ON v.venue_id = r.venue_id
JOIN events e ON e.reservation_id = r.reservation_id
WHERE e.date_of_use NOT IN ('11-11-2018', '11-12-2018')
Update 1
Based on your comment, you have to implode the array with ','.
$dates = implode( "','", $date );
And then the below WHERE clause will work perfect,
WHERE e.date_of_use NOT IN ('$dates')
Update 2
If events and reservations tables are empty, then you need to use LEFT JOIN and fetch rows where date_of_use IS NULL
SELECT * FROM venues v
LEFT JOIN reservations r ON v.venue_id = r.venue_id
LEFT JOIN events e ON e.reservation_id = r.reservation_id
WHERE e.date_of_use NOT IN ('$dates')
OR e.date_of_use IS NULL
If $dates variable as you say, is a string with comma separated dates "11-11-2018, 11-12-2018" the query becomes:
WHERE e.date_of_use NOT IN ('11-11-2018, 11-12-2018')
whereas the right format would be:
WHERE e.date_of_use NOT IN ('2018-11-11', '2018-11-12')
i.e. each date should be intoduced separately inside IN and the date format is YYYY-MM-DD.
To handle also the case where events do not exists for a venue:
SELECT * FROM venues v
JOIN reservations r ON v.venue_id = r.venue_id
LEFT JOIN events e ON e.reservation_id = r.reservation_id
AND (e.date_of_use IS NULL OR e.date_of_use NOT IN ('2018-11-11', '2018-11-12'))
You can structure your array in php like this:
<?php
$DateArray=array();
$DateArray=["11-11-2018", "12-11-2018","11-11-2018", "11-12-2018"];
$Dates='(';
foreach ($DateArray as $key => $value)
{ $Dates=$Dates." ".$value.","; }
$Dates=rtrim($Dates,",");
$Dates=$Dates.")"; // contains ( 11-11-2018, 12-11-2018, 11-11-2018, 11-12-2018)
?>
And append the php variable like this:
SELECT * FROM venues v, reservations r, events e
WHERE v.venue_id = r.venue_id
AND e.reservation_id = r.reservation_id AND e.date_of_use NOT IN .$Dates;
Why don't use simplfy your query for debug?
Something like
SELECT * FROM events e
WHERE e.date_of_use NOT IN ('11-11-2018', '11-12-2018')
Replace dates array using implode function in PHP.
Also you would better use date type rather than text.
https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_date-format
My sql:
SELECT * FROM ex_pair
LEFT JOIN ex_currency as client
ON client_curr = currency_id
LEFT JOIN ex_currency as company
ON co_curr = currency_id
I need to get data for two currency_id but I have an error
ambiguous column name: 'currency_id'
Is there any way to do it right or i need to use two queries?
You need to include your alias in your join, like this:
SELECT *
FROM ex_pair
LEFT JOIN ex_currency AS client
ON client_curr = client.currency_id
LEFT JOIN ex_currency as company
ON co_curr = company.currency_id
You may also want to do something other than select * as you will have two tables with the same columns - something like
SELECT pair.*, company.currency_id AS company_currency_id, client.currency_id as client_currency_id, [...]
FROM ex_pair AS pair
[...]
This way when you explicitly declare the columns you intend to use from ex_currency with an alias, you can know on the other end more easily which are client and which are company. You will need to do this for each column in the currency table that you want in your result, though that can be done if you have your table structure in your code easily enough by looping over the list of columns and appending the alias.
$array = [
1=> "currency_id",
2=> "currency_name"
];
$columns = ""
foreach($array as $column){
$columns.= "company.".$column." AS company_".$column;
$columns.= ",client.".$column." AS client_".$column.",";
}
$columns = rtrim($columns,',');
Gives you
company.currency_id AS company_currency_id,client.currency_id AS client_currency_id,company.currency_name AS company_currency_name,client.currency_name AS client_currency_name
Add that after your SELECT pair.* and you get your columns from the currency table, aliased so you know which is which.
you can use the alias that you give to the tables:
SELECT client.currency_id as client_currency, company.currency_id as company_currency
FROM ex_pair
LEFT JOIN ex_currency as client ON client_curr = client.currency_id
LEFT JOIN ex_currency as company ON co_curr = company.currency_id
I got the bellow piece of select statement that got level 2 child records, having problems to got deeper, can anyone help out?
SELECT
id_mobile AS ID_PROJETO,
UM.qtd_UC,
AM.qtd_AMBIENTE
FROM projetos_mobile AS PM
LEFT JOIN (
SELECT
COUNT(id) AS qtd_UC,
projeto,
data_hora_importacao,
id_uc_mobile
FROM ucs_mobile
WHERE data_hora_importacao = '2015-05-15 17:21:02'
GROUP BY projeto) AS UM
ON PM.id_mobile = UM.projeto
LEFT JOIN (
SELECT
COUNT(id_uc_mobile) AS qtd_AMBIENTE,
id_uc_mobile
FROM ucs_mobile
LEFT JOIN (
SELECT
uc
FROM ambientes_mobile AS s
WHERE data_hora_importacao = '2015-05-15 17:21:02') AS G
ON G.uc = ucs_mobile.id_uc_mobile
WHERE data_hora_importacao = '2015-05-15 17:21:02') AS AM
ON UM.id_uc_mobile = AM.id_uc_mobile
WHERE PM.data_hora_importacao = '2015-05-15 17:21:02'
http://sqlfiddle.com/#!9/2eecf
here is a sqlfiddle if anyone want to try a solution. I have the specific hierarchy: projeto>uc>ambiente>secao>medicoes
ucs_mobile.projeto refers to projetos_mobile.id_mobile
ambientes_mobile.uc refers to ucs_mobile.id_uc_mobile
secoes_iluminacao_mobile.ambiente refers to ambientes_mobile.id_ambiente_mobile
I need a count of each child for the parent I pass, I will have 5 functions that
return the count of each child for a given parent, for example, for a projeto parent I should have count(ucs),count(ambientes),count(secoes),count(medicoes)
So, hope you guys can help me. The database is terrible ugly but that's is what I got. Appreciate any help.
When you have really large queries like this, it can often be helpful to break them down individually, starting from the ground up and patching them together.
I started by just getting the count of each ucs_mobile row for each projetos_mobile value. You can do that by joining the two tables on the related row, and using COUNT(DISTINCT um.id) to get the number of rows. There are other ways to do it, but this particular method will scale better for the rest of your query:
SELECT pm.id, COALESCE(COUNT(DISTINCT um.id), 0) AS qty_uc
FROM projetos_mobile pm
LEFT JOIN ucs_mobile um ON um.data_hora_importacao = '2015-05-15 17:21:02' AND um.projeto = pm.id_mobile
GROUP BY pm.id;
The COALESCE function will be used to fill 0 counts. As long as you remember to use the DISTINCT keyword, and group by the proper id, you can just add in the child rows like so:
SELECT
pm.id,
COALESCE(COUNT(DISTINCT um.id), 0) AS qty_uc,
COALESCE(COUNT(DISTINCT am.id), 0) AS qty_am,
COALESCE(COUNT(DISTINCT sim.id), 0) AS qty_sim
FROM projetos_mobile pm
LEFT JOIN ucs_mobile um ON um.data_hora_importacao = '2015-05-15 17:21:02' AND um.projeto = pm.id_mobile
LEFT JOIN ambientes_mobile am ON am.data_hora_importacao = um.data_hora_importacao AND am.uc = um.id_uc_mobile
LEFT JOIN secoes_iluminacao_mobile sim ON sim.data_hora_importacao = am.data_hora_importacao AND sim.ambiente = am.id_ambiente_mobile
GROUP BY pm.id;
Here is an SQL Fiddle example. NOTE I changed your sample data slightly to ensure my query was working as expected.
Also, a side note. I noticed as you went along that you kept using the same date in your WHERE clauses, so I just joined each table on the date as well, and made sure that in my very first join I looked for the date specified, which in turn will carry its way over to the other tables.
I keep falling back into questions with MySQL joining.
And I would like to request a very simple example I could use to continue my journey of understanding learning the MySQL syntax.
Let's say I got the following table's
test_testtable
testtable_id
testtable_name
testtable_user
testtable_option
testtable_textfield
test_testlink
testlink_id
testlink_link
testlink_address
test_address
address_id
address_name
address_phone
address_email
address_street
address_city
address_zip
I would like to make a selection like :
SELECT * (lets say I would define the fields) FROM `test_testable`
JOIN `test_testtable`.`testtable_id` = `test_testlink`.`testlink_link`
AND
JOIN `test_testlink`.`testlink_addres` = `test_address`.`address_id`
WHERE `user_id` = 5
Hence the linking structure is like:
test_testtable.testtable_id = leading
table test_testlink is a table to link the table test_testtable and test_address
And linking table test_testlink uses the field testlink_link to link to the table test_testtable, and uses the field testlink_address to link to the table test_address
This does not work. FOR ME.. Since I continuously seem to fail of catching the correct syntax logic.
So I hope that someone could give me a small example of how to correctly implement such a simple yet critical query!
TIAD!!
A general approach :
SELECT table1.* FROM table1
JOIN table2 ON table2.id_table1 = table1.id
JOIN table3 ON table3.id_table2 = table2.id
WHERE table1.id = 10
For your purpose :
SELECT * (lets say I would define the fields) FROM `test_testable`
JOIN `test_testlink` ON `test_testtable`.`testtable_id` = `test_testlink`.`testlink_link`
JOIN `test_address` ON `test_testlink`.`testlink_addres` = `test_address`.`address_id`
WHERE `user_id` = 5
Please read the reference
You are using wrong syntax. You should mention which tables to join first then based on which fields.
SELECT * (lets say I would define the fields) FROM `test_testable`
INNER JOIN test_testlink
ON `test_testtable`.`testtable_id` = `test_testlink`.`testlink_link`
INNER JOIN `test_address`
ON `test_testlink`.`testlink_addres` = `test_address`.`address_id`
AND `test_testtable`.`user_id` = 5
select * from testlink JOIN testtable ON testlink.tableid = testtable.ID
JOIN testaddress ON testlink.addressid = testaddress.ID
WHERE testtable.ID = 5
Let's say I have this query:
<?
$qi = $db->prepare('SELECT one.id, one.Value, two.Name, three.nfid, temp.Name AS Alias
FROM one
INNER JOIN two ON one.fid = two.id
LEFT OUTER JOIN three ON two.fid = three.fid
LEFT OUTER JOIN (SELECT id,Name FROM two) AS temp ON three.nfid = temp.id
WHERE one.rid = ?
ORDER BY one.id ASC');
$qi->execute( array( $id ) );
?>
Connections between the tables are:
Table one contains a number of rows with the fields one.Value, one.rid and one.fid.
fid is a connection to table two which contains the two.Name of the items (one.fid = two.id).
But sometimes the item is an alias for another item, which is why table three exists. It contains the fields three.fid and three.newfid where three.newfid = two.id (but for another item with another two.Name)
The query is supposed to fetch all rows from one with a certain one.rid and get one.Value, two.Name and if there is an three.fid for this one.fid, get two.Name for three.newfid and call it Alias.
Is there a way to improve this query or solve the problem in another way? Perhaps reshape the layout of the database? It is currently quite slow. The example here have been simplified to make it more general.
Thank you.
The subquery in parentheses forces MYSQL to ignore its indexes, which makes it take a long time. Better to directly join two as temp. As long as you always put two.[field] and temp.[field], it will tell them apart just fine.
SELECT one.id, one.Value, two.Name, three.nfid, temp.Name AS Alias
FROM one
INNER JOIN two ON one.fid = two.id
LEFT OUTER JOIN three ON two.fid = three.fid
LEFT OUTER JOIN two AS temp ON three.nfid = temp.id
WHERE one.rid = ?
ORDER BY one.id ASC