MySQL NOT IN function of array of data - php

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

Related

Getting data from a table even if there's no data in table 2 that links to table 1

I'm creating a planner but the SQL statement I have now only shows employees that have something in table 2 (plannerEntries) and doesn't show the rest of the employees from table 1 (Employee_List).
I need all of the employees to be outputted into the table regardless of whether they have any jobs assigned for them for the week, so that they can have new jobs assigned easily.
This is my current SQL code
SELECT [EL].[Employee_Numer],
[PP].[workDate],
[PP].[jobNo],
[PP].[workDescription],
[PP].[timeOfDay],
[JF].[Description],
[EL].[Forename],
[EL].[Surname]
FROM plannerEntries AS PP
RIGHT JOIN [Employee_List] AS EL
ON [PP].[employeeNumber] = [EL].[Employee_Numer]
INNER JOIN [Job File] AS JF
ON [PP].[jobNo] = [JF].[Job No]
WHERE [PP].[workDate] >= '$monday'
AND [PP].[workDate] <= '$sunday'
ORDER BY [PP].[employeeNumber] ASC;
I expected all employees to be printed regardless of records in table 2, however only the employees with records in table 2 were printed. The below image is the actual output.
Please check the difference between inner join, left join, right join.
Something like this should do what you need:
SELECT
[EL].[Employee_Numer],
[PP].[workDate],
[PP].[jobNo],
[PP].[workDescription],
[PP].[timeOfDay],
[JF].[Description],
[EL].[Forename],
[EL].[Surname]
FROM
[Employee_List] AS EL
left join plannerEntries AS PP on [PP].[employeeNumber] = [EL].[Employee_Numer]
and [PP].[workDate] >= '$monday'
and [PP].[workDate] <= '$sunday'
left join [Job File] AS JF on [JF].[Job No] = [PP].[jobNo]
ORDER BY
[PP].[employeeNumber] ASC;

Double LEFT JOIN with same tables

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

Retrieve latest timestamp row from table using INNER JOIN

want to retrieve the latest timestamp of the row of name from table. However only the table "location" has the timestamp.
I used INNER JOIN to join the tables "elderly1" and "location" together. i am only able to show that i have retrieved the latest timestamp but not the latest "latitude" and "longitude" from the table "location". Please help.
Query
SELECT e.name,e.illness, e.patient_id,e.patient_image,e.area, e.arduino_mac,
l.arduino_mac, l.latitude, l.longitude,MAX(l.timestamp) as ts
FROM elderly1 e
JOIN location l
on e.arduino_mac = l.arduino_mac
WHERE e.arduino_mac = l.arduino_mac
GROUP BY e.name
ORDER BY l.timestamp
It's difficult to say much without knowing the candidate keys of each table, but in general you must make sure that the SELECT clause is functionally dependent of the GROUP BY clause. Given you formulation of the problem I would suggest something like:
SELECT e.name,e.illness, e.patient_id,e.patient_image,e.area, e.arduino_mac,
l.arduino_mac, l.latitude, l.longitude, l.timestamp as ts
FROM elderly1 e
JOIN ( SELECT l1.arduino_mac, l1.latitude, l1.longitude, l1.timestamp
FROM location l1
WHERE timestamp = ( SELECT MAX(timestamp)
FROM LOCATION l2
WHERE l1.arduino_mac = l2.arduino_mac )
) as l
on e.arduino_mac = l.arduino_mac
ORDER BY l.timestamp
If the (arduino_mac,timestamp) tuple is unique in the location table, you could do something like this:
SELECT e.name
, e.illness
, e.patient_id
, e.patient_image
, e.area
, e.arduino_mac
, l.arduino_mac
, l.latitude
, l.longitude
, l.timestamp
FROM elderly1 e
JOIN ( SELECT d.arduino_mac
, MAX(d.timestamp) AS latest_ts
FROM location d
GROUP BU d.arduino_mac
) f
ON f.arduino_mac = e.arduino_mac
JOIN location l
ON l.arduino_mac = f.arduino_mac
AND l.timestamp = f.lastest_ts
GROUP BY e.name
ORDER BY l.timestamp
The inline view f gets the "latest timestamp" for each value of arduino_mac. (Performance of the view query will be best if there's a suitable index available, e.g.
... ON location (arduino_mac,timestamp)
We can use that value of timestamp to retrieve the other columns on that row, with a join to the location table.
Note that if there are two (or more) rows with the same latest "timestamp" value for a given arduino_mac, then this query will retrieve all of the rows with that matching timestamp, and it will be indeterminate which of those rows will remain after the GROUP BY operation. (If we're guaranteed (arduino_mac,timestamp) is unique, this won't be an issue. In the more general case.)
In the same way, if there are multiple rows with the same name value in elderly, with different values of arduino_mac, it's indeterminate which of the matching location rows is retrieved, and returned.

right outer join between 2 tables

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.

Using JOIN to match 1st result

I'm trying to obtain the price from a field called DiscountMarkupPriceRate1 on my second table to display in PHP.
The first portion of my query pulls what I need correctly, the Parentsku of all visible products with inventory. ie GTR101.
I'm trying to join it with a second table and retrieve only the first DiscountMarkupPriceRate1 for the parent (GTR101%) where ItemCustomerPriceLevel is M.
Here's what my table looks like. This is essentially the result of the first half of my query before the join (stripped of all the other fields I need):
**INVENTORY**
SKU store_quantity parent_sku visible
----------------------------------------------------------------
GTR101 20 NULL Y
GTR102 100 NULL Y
GTR103 88 NULL Y
This is the second table:
**ins_imb_1**
DiscountMarkupPriceRate1 ItemNumber ItemCustomerPriceLevel
-----------------------------------------------------------------
15.950 GTR101S M
15.950 GTR101M M
11.950 GTR101L M
10.000 GTR101S T
I'm trying to get
GTR101 15.95
and here's what I have for a query:
Select *
from INVENTORY
where parent_sku=''
AND store_quantity > 0
AND SKU like '%GTR%'
AND visible='Y'
LEFT JOIN ins_imb_1
ON ins_imb_1.ItemNumber =
(
SELECT ItemNumber, ItemCustomerPriceLevel, DiscountMarkupPriceRate1
FROM ins_imb_1
WHERE ins_imb_1.ItemNumber% = INVENTORY.SKU
AND ins_imb_1.ItemCustomerPriceLevel = 'M'
ORDER BY
INVENTORY.SKU
LIMIT 1
)
First thing wrong I see here, is that JOINs need to be after the FROM statement and before WHERE.
Also your subquery in the LEFT JOIN is wrong.
LEFT JOIN ins_imb_1
ON ins_imb_1.ItemNumber =
(
SELECT ItemNumber, ItemCustomerPriceLevel, DiscountMarkupPriceRate1
Your subquery should only return one field (for it to compare to ins_imb_1.ItemNumber).
I don't know if you even need a subquery here, you could do something like this:
LEFT JOIN ins_imb_1
ON ins_imb_1.ItemNumber LIKE CONCAT(INVENTORY.SKU, '%')
AND ins_imb_1.ItemCustomerPriceLevel = 'M'
I also see few things that could be optimized here.
where parent_sku='' should be where parent_sku IS NULL.
AND SKU like '%GTR%' should be AND SKU like 'GTR%'(as the SKU always start with 'GTR').
SELECT *
FROM INVENTORY
LEFT JOIN ins_imb_1
ON ins_imb_1.ItemNumber LIKE CONCAT(INVENTORY.SKU, '%')
AND ins_imb_1.ItemCustomerPriceLevel = 'M'
WHERE parent_sku IS NULL
AND store_quantity > 0
AND SKU LIKE 'GTR%'
AND visible = 'Y'

Categories