I have a query which uses LEFT JOIN with multiple criteria to fetch the data. However, the query does not return any data when I use more than one AND clause in the conditions.
Eg:
LEFT JOIN tblName
on x.ABC=y.ABC
AND x.MNO="AnyValue"
AND x.UserId=1
does not return any data.
However when I remove the last AND clause (i.e LEFT JOIN tblName on x.ABC=y.ABC AND x.MNO="AnyValue"), it gives me wrong result. But yes it atleast return some data.
Let me know how to add multiple conditions to get the query working.
I want to get results using the above first condition, i.e LEFT JOIN tblName on x.ABC=y.ABC AND x.MNO="AnyValue" AND x.UserId=1
Your AND ... criteria needs to be in the WHERE part of the query.
First, lets make sure you understand what a LEFT JOIN is. Try to think of it this way. The left side is how I think in the sequential order of the FROM clause. You read left-to-right, so the first table is my LEFT side which is what I ALWAYS want. The right side (next table in the query) is optional, but if it exists, great, I have more detail.
So take a scenario of people and orders. I want a list of all people. But if they have an order, thats even better.
select
P.LastName,
O.OrderID,
O.OrderDate
from
People P
LEFT JOIN Order O
on P.PersonID = O.PersonID
This will do just that. Show all people and IF they have an order, that comes along for the ride. If they have multiple orders, it will show a row for ALL orders they had.
Now, expand on criteria to the next table. Lets say I only wanted to show orders since 2018. That would become part of the LEFT JOIN "ON" clause because I only care about orders portion of the requirement.
LEFT JOIN Order O
on P.PersonID = O.PersonID
AND O.OrderDate >= '2018-01-01'
I would still get ALL people, but only order info will show for those with an order date on/after Jan 1, 2018.
Now, if you only cared about ONLY PEOPLE who HAD ORDERS AND the orders were on after 2018, that would basically eliminate the need for a LEFT JOIN and become a standard (INNER) JOIN
JOIN Order O
on P.PersonID = O.PersonID
AND O.OrderDate >= '2018-01-01'
JOIN Implies the record in the ORDER side table MUST be found
So in your scenario, where is your "x" and "y" alias name coming from. Completely unclear given the context, but hopefully the above clarification will help you resolve your query. If not, update it with more detail and clarification what you are trying to get out of it.
Related
I have the performance problem with query that have order by and group by. I have checked similar problems on SO but I did not find the solution to this:(
I have something like this in my db schema:
pattern has many pattern_file belongs to project_template which belongs to project
Now I want to get projects filtered by some data(additional tables that I join) and want to get the result ordered for example by projects.priority and grouped by patterns.id. I have tried many things and to get the desired result I've figured out this query:
SELECT DISTINCT `projects`.* FROM `projects`
INNER JOIN `project_templates` ON `project_templates`.`project_id` = `projects`.`id`
INNER JOIN `pattern_files` ON `pattern_files`.`id` = `project_templates`.`pattern_file_id`
INNER JOIN `patterns` ON `patterns`.`id` = `pattern_files`.`pattern_id`
...[ truncated ]
INNER JOIN (SELECT DISTINCT projects.id FROM `projects` INNER JOIN `project_templates` ON `project_templates`.`project_id` = `projects`.`id`
INNER JOIN `pattern_files` ON `pattern_files`.`id` = `project_templates`.`pattern_file_id`
INNER JOIN `patterns` ON `patterns`.`id` = `pattern_files`.`pattern_id`
...[ truncated ]
WHERE [here my conditions] ORDER BY [here my order]) P
ON P.id = projects.id
WHERE [here my conditions]
GROUP BY patterns.id
ORDER BY [here my order]
From my research I have to INNER JOIN with subquery to conquer the problem "ORDER BY before GROUPing BY" => then I have put the same conditions on the outer query for performance purpose. The order by I had to use again in the outer query too, otherwise the result will be sorted by default.
Now there is real performance problem as I have about 6k projects and when I run this query without any conditions it takes about 15s :/ When I narrow the result by specify the conditions the time drastically dropped down. I've found somewhere that the subquery is run for every outer query row result which could be true when you watch at the execution time :/
Could you please give some advice how I can optimize the query? I do not work much with sql so maybe I do it from the wrong side from the very beginning?
P.S. I have tried WHERE projects.id IN (Select project.id FROM projects ....) and that discarded the performance issue but also discarded the ORDER BY before GROUPing BY
EDIT.
I want to retrieve list of projects, but I want also to filter it and order, and finally I want to get patterns.id unique(that is why I use the group by).
order by in your inner query (p) doesn't make sense (any inner sort will only
have an arbitrary effect).
#Solarflare Unfortunately it does. group by will take first row from grouped result. It preserve the order for join. Well, I believe that it is specific to MySql. Furthermore to keep the order from subquery I could use ORDER BY NULL in outer query :-)
Also, select projects.* ... group by pattern.id is fishy (although MySQL, in contrast to every other dbms, allows you to do this)
so we can assume I retrieve only projects.id, but from docs:
MySQL extends the use of GROUP BY to permit selecting fields that are not mentioned in the GROUP BY clause
I have been trying to get the result of this query to include all the flavor.model and sum the quantity of the ones ordered. It does work except it does not show anythign from the flavor table when nothing was ordered. I though left join would include all the item from the left side?
select
flavor.id,
flavor.model,
IFNULL(SUM(orders.quantity),0)as ORDERED
from
orders
LEFT JOIN
flavor
ON orders.model=flavor.model
where
orders.orddate >= '131006'
and orders.orddate <= '131013'
and orders.buyer<>'****#gmail.com'
GROUP BY
flavor.model
You might want to do a RIGHT JOIN instead of a LEFT JOIN as your left column, orders is your reference table, hence you won't get flavour values which aren't found.
So, alright, I have a few tables. My current query runs against a "historical" table. I want to do a join of some kind to get the most recent status from my Current table. These tables share a like column, called "ID"
Here's the structure
ddCurrent
-ID
-Location
-Status
-Time
ddHistorical
-CID (AI field to keep multiple records per site)
-ID
-Location
-Status
-Time
My goal now is to do a simple join to get all the variables from ddHistorical and the current Status from ddCurrent.
I know that they can be joined on ID since both of them have the same items in their ID tables, I just can't figure out which kind of join is appropriate or why?
I'm sure someone may provide a specific link that goes into great detail explaining, but I'll try to summarize it this way. When writing a query, I try to list the tables from the position of what table do I want to get data from and have that as my first table in the "FROM" clause. Then, do "JOIN" criteria to other tables based on relationships (such as IDs). In your example
FROM
ddHistorical ddH
INNER JOIN ddCurrent ddC
on ddH.ID = ddC.ID
In this case, INNER JOIN (same as JOIN) the ddHistorical table is the left table(listed first for my styling consistency and indentation) and ddCurrent is the right table. Notice my ON criteria that joins them together is also left alias.column = right alias table.column -- again, this is just for mental correlation purposes.
an Inner Join (or JOIN) means a record MUST have a match on each side, otherwise it is discarded.
A LEFT JOIN means give me all records in the LEFT table (ddHistorical in this case), regardless of a matching in the right-side table (ddCurrent). Not practical in this example.
A RIGHT JOIN is the reverse... give me all records from the RIGHT-side table REGARDLESS of a matching record in the left side table. Most of the time you will see LEFT-JOINs more frequently than RIGHT-JOINs.
Now, a sample to mentally get the left-join. You work at a car dealership and have a master table of 10 cars that are sold. For a given month, you want to know what IS NOT selling. So, start with the master table of all cars and look at the sales table for what DID sell. If there is NO such sales activity the right-side table will have NULL value
select
M.CarID,
M.CarModel
from
MasterCarsList M
LEFT JOIN CarSales CS
on M.CarID = CS.CarID
AND month( CS.DateSold ) = 4
where
CS.CarID IS NULL
So, my LEFT join is based on a matching car ID -- AND -- the month of sales activity is 4 (April) as I may not care about sales for Jan-Mar -- but would also qualify year too, but this is a simple sample.
If there is no record in the Car Sales table it will have a NULL value for all columns. I just happen to care about the car ID column since that was the join basis. That is why I am including that in the WHERE clause. For all other types of cars that DO have a sale it will have a value.
This is a common approach you will see in querying where someone looking for all regardless of other... Some use a where NOT EXIST ( subselect ), but those perform slower because they test on every record. Having joins is much faster.
Other examples may be you want a list of all employees of a company, and if they had some certification / training to show it... You still want all employees, but LEFT-JOINING to some certification/training table would expose those extra field as needed.
select
Emp.FullName,
Cert.DateCertified
FROM
Employees Emp
Left Join Certifications Cert
on Emp.EmpID = Cert.EmpID
Hopefully these samples help you understand better the relationship for queries, and now to actually provide answer for your needs.
If what you want is a list of all "Current" items and want to look at their historical past, I would use current FIRST. This might be if your current table of things is 50, but historically your table had 420 items. You don't care about the other 360 items, just those that are current and the history of those.
select
ddC.WhateverColumns,
ddH.WhateverHistoricalColumns
from
ddCurrent ddC
JOIN ddHistorical ddH
on ddC.ID = ddH.ID
If there is always a current field then a simple INNER JOIN will do it
SELECT a.CID, a.ID, a.Location, a.Status, a.Time, b.Status
FROM ddHistorical a
INNER JOIN ddCurrent b
ON a.ID = b.ID
An INNER JOIN will omit any ddHistorical rows that don't have a corresponding ID in ddCurrent.
A LEFT JOIN will include all ddHistorical rows, even if they don't have a corresponding ID in ddCurrent, but the ddCurrent values will be null (because they're unknown).
Also note that a LEFT JOIN is just a specific type of outer join. Don't bother with the others yet - 90% or more of what you'll ever do will be INNER or LEFT.
To include only those ddHistorical rows where the ID is in ddCurrent:
SELECT h.CID, h.ID, h.Location, h.Status, c.Status, h.Time
FROM ddHistorical h
INNER JOIN ddCurrent c ON h.ID = c.ID
If you want to include ddHistorical rows even if the ID isn't in ddCurrent:
SELECT h.CID, h.ID, h.Location, h.Status, c.Status, h.Time
FROM ddHistorical h
LEFT JOIN ddCurrent c ON h.ID = c.ID
If all ddHistorical rows happen to match an ID in ddCurrent, note that both queries will return the same result.
Join Orders, Staff's first and last name, item and location into one so I can export the content into an Excel spreadsheet.
SELECT orders.order_id, staff.staff_id, staff.first_name, staff.last_name, items.name, locations.address1, locations.address2, locations.state, locations.zip_code, orders.created_at
FROM orders
INNER JOIN staff
ON orders.staff_id = staff.staff_id
INNER JOIN items
ON orders.item_id = items.item_id
INNER JOIN location_staff
ON location_staff.staff_id = staff.staff_id
INNER JOIN locations
ON location_staff.loc_id = location.loc_id
I am trying to gather this information to put into an excel document but my query is not returning any results. Any help would be appreciated.
Here is An ERD diagram for further understanding
https://www.dropbox.com/s/7inma4s42xq5t4a/ERD.jpg
(Location_staff_link was shortened when created to location_staff)
jsut remove the extra tables in your FROM clause,
SELECT orders.order_id,
staff.staff_id,
staff.first_name,
staff.last_name,
items.name,
orders.created_at
FROM orders
INNER JOIN staff
ON orders.staff_id = staff.staff_id
INNER JOIN items
ON orders.item_id = items.item_id
INNER JOIN locations
ON location.staff_id = staff.staff_id
To further gain more knowledge about joins, kindly visit the link below:
Visual Representation of SQL Joins
Nothing in your query looks bad, so here is what I suggest to find the answer in this sort of case.
First reduce to the bare minimum of the first join and change the fields to select * (temporarily, you can go back to the real fields as soon as you find the issue) so you can se all the data.
Do you get records, then add in any where clauses on the tables in that join. Do you still get records?
Then add in each join and each where clause one at a time until you find the one where the records disappear. This will tell you what you need to do to fix it.
Often in a case like this where no records are returned, one of the joins needs to be a left join or there is no data that meets the terms of the where clauses or one of the joins is improperly defined. And sometimes, there is a problem in that your database does not have the data you were expecting it to have. But first you have to use the process above to diagnose where the problem is.
With 1001 possibilities to use MySQL, I've come up with a requirement for myself that I want to figure out, but don't know how. I also ran a Google Search of course and checked Stack Overflow and MySQL Docs, but I didn't get the answer I was looking for.
The situation:
I have 2 tables. One called and containing customers and one containing customer_progress . For your imaging, the customer_progress table can optionally contain data about the progress/status for a customer.
My goal was to join the data from the two tables, and put the customers that do have progress data on top, followed by the customers that do not have progress data; all in 1 query.
So I started writing a query, much like the following:
SELECT
*
FROM
customers AS c
LEFT JOIN
customer_progress AS p
ON
p.customer_id = c.id AND p.year = 2011 // Joining them and selecting the progress for a certain year.
WHERE
c.active = 1;
How can I put the customers that do have a progress record first, and then afterwards all the customers that do not have a progress record?
I don't know if I am going the right way with the query or that I need to approach this from a whole different angle. Using a ORDER BY made no sense.
You need to select a customer progress_field that you can order by so that you add something like
order by custer_progress_field desc;
at the end of your select statement.
For example:
SELECT
c.filed1, p.field1
FROM
customers AS c
LEFT JOIN
customer_progress AS p
ON
p.customer_id = c.id AND p.year = 2011 // Joining them and selecting the progress for a certain year.
WHERE
c.active = 1;
Order by p.field1 desc;
You need to add something like
ORDER BY IF(p.customer_id IS NULL,1,0), [other fields]
// or you can do just if sorting by customer_id is acceptable
ORDER BY p.customer_id DESC
You can check more details about how columns with NULL values are treated by ORDER BY on http://dev.mysql.com/doc/refman/5.0/en/working-with-null.html