I have wrote 2 SQL statements.
SELECT
customers.CustomerID,
orders.OrderID
FROM customers LEFT JOIN orders ON customers.CustomerID =
orders.CustomerID AND orders.EmployeeID=4
WHERE orders.OrderID IS NULL;
And
SELECT
customers.CustomerID,
orders.OrderID
FROM customers LEFT JOIN orders ON customers.CustomerID =
orders.CustomerID AND orders.EmployeeID=4 AND orders.OrderID IS NULL;
The 1st one returns 16 entries, which is correct (per the solution). The 2nd one returns 91 entries. I am using Northwind for mysql version. I read from somewhere that placing condition in JOIN statement is equivalent to doing that in WHERE statement. However, here I can see the difference.
This is to do with the order execution on the statements and the fact you’re using a LEFT JOIN.
A LEFT JOIN will keep all the values from the left side intact, combining only values from the right side that match conditions.
A WHERE clause operates on the query as a whole (in this case).
Query 1:
Get all values from left table
Join on any values from the right table that match conditions
Filter the joined output based on the where condition
Query 2:
Get all values from the left table
Join on any values from the right table that match conditions
The only time a WHERE is really analogous to a JOIN is something like a INNER JOIN, which gets only the relevant values from both left and right sides. I think at least, it has been a while since I flexed the SQL muscles.
Edit - count the number of rows (nothing else) in your customers table, it should return 91 as well.
The second query will return more rows. Why? Read on:
Second query
The second query will match orders rows to customers rows using the predicate:
customers.CustomerID = orders.CustomerID AND orders.EmployeeID=4 AND orders.OrderID IS NULL
Unmatched customers rows will always show up in the result.
First query
Now, the first query will match orders rows to customers rows using the predicate:
customers.CustomerID = orders.CustomerID AND orders.EmployeeID=4
Then it will filter out rows where orders.OrderID is null, removing customer rows that do not fullfil this last predicate, therefore producing less rows. Unmatched customers rows may not show up in the result.
Example:
create table customers (
CustomerId int
);
create table orders (
OrderId int,
CustomerId int,
EmployeeId int
);
insert into customers (CustomerId) values (1), (2), (3);
insert into orders (OrderId, CustomerId, EmployeeId) values
(1001, 1, 3),
(1002, 1, 4),
(1003, 2, 1);
The first query returns:
CustomerId OrderId
---------- ----------------
2 <null>
3 <null>
while the second query returns:
CustomerId OrderId
---------- ----------------
1 <null>
2 <null>
3 <null>
This could be because of the order in which the Sql query runs
FROM and JOIN s. The FROM clause, and subsequent JOIN s are first executed then
WHERE and lastly SELECT.
In second case all the join conditions ran together but in the first case the where clause made the difference after joining the data and filtering not at the same time but as a whole after joining.
Related
I have my two MYSQL database:
database 1 with table: Employee with the columns:
Employee_code and
Employee _ID
and database 2 with table: Employee_log with the columns:
Date,
Time and
Employee_ID.
I have now, created a third database 3 with table: Employee_Detailswith the columns:
Employee_code
Employee_ID
Date and
Time.
I have also managed to insert date to the Employee_Details table using the following SQL Query:
INSERT INTO database3.Employee_Details(Employee_ID, Employee_Code)
SELECT Employee_ID, Employee_Code FROM database 1.Employee;
but, now I want to include the Date and Time from database 2 table: Employee_log to the database 3 with the matching employee ID. I tried using the above code but the dates are getting inserted into the table after the Employee_ID in a new row with other column values NULL.
Example:
Employee_ID Employee_Code Date
1 1011
2 1012
Null Null 09/05/2018
Null Null 08/05/2018
So, how can I insert the date to its matching Employee_id, can someone please help me with this.
Thanks,
You could start over by first deleting all rows in database3.Employee_Details.
DELETE FROM database3.Employee_Details;
Next, do an INSERT ... SELECT like you did but join database1.Employee and database2.Employee_log this time.
INSERT INTO database3.Employee_Details
(Employee_ID,
Employee_Code,
Date,
Time)
SELECT Employee.Employee_code,
Employee.Employee_ID,
Employee_log.Date,
Employee_log.Time
FROM database1.Employee
LEFT JOIN database2.Employee_log
ON Employee_log.Employee_ID = Employee.Employee_ID;
In general a join combines the rows of two tables. Each row of the right table is appended to the row of the left table if the condition in the ON clause is true for that pair of rows.
There are some different flavors of joins. Here we use a LEFT JOIN. In a LEFT JOIN the rows of the left table (the table left of the JOIN keyword) are included in the result, even if there were no matching rows from the right table. Instead of the column values from the right table, the respective columns in the result are all null, as there are no values from matching rows.
In contrast a INNER JOIN has only those rows of the left table included where a matching row from the right table exists.
To include all employees, even if no log data was available for them, we had to choose a LEFT JOIN here.
If you don't want to start over, you can update database3.Employee_Details with the missing values.
UPDATE database3.Employee_Details
LEFT JOIN database1.Employee_log
ON Employee_log.Employee_ID = Employee_Details.Employee_ID
SET Employee_Details.Date = Employee_log.Date,
Employee_Details.Time = Employee_log.Time;
Again we use a LEFT JOIN and set the fields in the SET clause with the respective values from the result of the join.
Afterwards delete the unnecessary rows with empty Employee_ID.
DELETE FROM database3.Employee_Details
WHERE Employee_Details.Employee_ID IS NULL;
You can do the insert as:
INSERT INTO database3.Employee_Details(Employee_ID, Employee_Code)
SELECT e.Employee_ID, e.Employee_Code, el.date
FROM database 1.Employee e LEFT JOIN
Employee_log el
ON e.Employee_ID = el.Employee_ID;
You might find it simpler to just create the table as:
CREATE TABLE database3.Employee_Details as
SELECT e.Employee_ID, e.Employee_Code, el.date
FROM database 1.Employee e LEFT JOIN
Employee_log el
ON e.Employee_ID = el.Employee_ID;
I have a query where I output some results
SELECT
t1.busName,
t1.busCity,
COUNT(t2.ofr_id) AS cntOffers
FROM t1
LEFT JOIN t2 ON (t2.ofr_busID = t1.busID)
The query above returns only one row, however, if I remove COUNT and leave only below query I get multiple results. What am I missing? And how can I fetch results from the first table while getting associated results count from t2?
SELECT
t1.busName,
t1.busCity
FROM t1
LEFT JOIN t2 ON (t2.ofr_busID = t1.busID)
You need group by:
SELECT t1.busName, t1.busCity,
COUNT(t2.ofr_id) AS cntOffers
FROM t1 LEFT JOIN
t2
ON t2.ofr_busID = t1.busID
GROUP BY t1.busName, t1.busCity;
Most databases would return an error on your version of the query, because you have unaggregated and aggregated columns in the SELECT.
It actually seems that the COUNT() in your first query is forcing a GROUP BY (because of the aggregation) on that field, which explains why you get only one row, but that does not imply that you only have one row in it.
Check out this SQL Fiddle
MySQL 5.6 Schema Setup:
CREATE TABLE TestData (a int, b int);
INSERT INTO TestData
(a, b)
VALUES
(1, 1),
(2, 2),
(3, 3);
Query:
SELECT a, count(b) from TestData
Results:
| a | count(b) |
|---|----------|
| 1 | 3 |
As Gordon Linoff suggested, you need to use GROUP BY explicitly in order to replicate the same behavior without the COUNT.
I have 200+ telephone numbers in Table A which I require address data for from Table B. Table B has 5 million+ rows of data and the matching field is PhoneNumber. I have written some SQL which does part of what I want but it is only matching up the first record in Table A and I have null values for all of the others???
Please help
SELECT TableA.TelephoneNumber, TableB.Address
FROM TableA LEFT OUTER JOIN
TableB
ON TelephoneNumber = PhoneNumber
If you only want matching records, you should use an inner join, not an outer join:
SELECT TableA.TelephoneNumber, TableB.Address
FROM TableA
JOIN TableB ON TelephoneNumber = PhoneNumber
I have a table, one column of which is VERY dependant on a bunch of other tables
i can't alter the table, and i can't change the name of the parameter that is used
this is my query :
select *,(select sum(itemQuantity*(select itemPrice from Items where
Items.itemID=OrderItems.itemID)) from OrderItems
where OrderItems.orderNumber=Orders.orderNumber) as orderValue,
(select sum(itemQuantity) from OrderItems where OrderItems.orderNumber=Orders.orderNumber)
as orderItemQuantity from Orders WHERE Orders.customerId =1 AND Orders.beenSupplied =1
and this is the result of this query:
as you can see, i have the column "orderValue" twice
the first orderValue is the original column from the Orders table, and the second orderValue is from the "as" clause in the query
how do i merge the two columns and have the output of the query in it, and not the 0 that constantly gets inserted ?
EDIT:
table structures:
Rather than using SELECT *, explicitly list the columns (from your Orders table) that you wish to select.
You can also avoid using (the highly inefficient) correlated subqueries by joining the tables in the outermost query and then grouping each order.
SELECT Orders.orderNumber,
Orders.customerId,
Orders.orderDate,
Orders.beenSupplied,
Orders.purchaseDate,
SUM(OrderItems.itemQuantity * Items.itemPrice) AS orderValue,
SUM(OrderItems.itemQuantity) AS orderItemQuantity
FROM Orders
JOIN OrderItems USING (orderNumber)
JOIN Items USING (itemID)
WHERE Orders.customerId = 1
AND Orders.beenSupplied = 1
GROUP BY Orders.orderNumber
I have an ordering system that can have multiple receipts related to one order. I recently ran into a query as follows that produced an undesirable result.
SELECT info FROM orders WHERE id IN (1, 2, 2) ORDER BY FIELD (id, 1, 2, 2);
Is there a way to return the row for order #2 twice? As of right now the query returns row one then row two as expected; however, in this particular instance returning row #2 twice is needed.
The tables are roughly as follows (I know it isnt totally valid MySQL, just for illustration):
CREATE TABLE orders (
id int(),
info VARCHAR(),
)
CREATE TABLE links (
orderid int(),
receiptid int()
)
CREATE TABLE receipts (
id int(),
otherinfo VARCHAR(),
)
If I'm understanding the situation correctly, you have two entries in the orders table
but orderId 2 is listed twice in the links table. If that is correct, then what you want is:
select o.info from orders o
inner join links l on o.id = l.orderid
If you need to return the row twice, then filtering in the where clause is not what you want. You can do this by filtering using a join:
SELECT o.info
FROM orders o join
(select 1 as id union all select 2 union all select 2
) ids
on o.id = ids.id
ORDER BY FIELD (o.id, 1, 2, 2);
Well, you coul make use of a UNION ALL
Something like
SELECT info FROM orders WHERE id IN (1, 2)
UNION ALL
SELECT info FROM orders WHERE id IN (2)