mysql left join after inner join gives wrong record set - mysql

SELECT
op.sub_order_id,
s.supplier_id,
GROUP_CONCAT(opb.box_id SEPARATOR ','),
op.delivery_country
FROM
order_p op
INNER JOIN
supplier s ON s.supplier_id = op.supplier_name
LEFT JOIN
order_boxes opb ON op.sub_order_id = opb.sub_order_id
WHERE
op.order_active=0
AND op.ship_date>='2013-01-01'
AND op.ship_date<='2013-04-24'
ORDER BY op.ship_date DESC
I am not very good at joins, so bear with me.
I have this query where I need to select all data from order table between the given date range, such that a matching supplier mapping exists in the supplier table. Now along with this info, I need to also fetch any boxes, if it exists in the order_boxes table linked by sub_order_id fields.
Now, if I just join the order and supplier tables, I get the correct record-set of about 1000 records, but as soon as I try to add in the boxes table, I get only one row. I am guessing this happens because there is only one record in the boxes table, but I need it to fetch all records from order table along with the box table. If I try INNER JOIN instead of LEFT JOIN I get only 1 record of the matching suborder as mapped in the box table, but with Left join, I get the first record within the date range, irrespective of the sub_order_id and the value in the boxes table.
Please help. Also if you need any more info, please let me know.
EDIT : I am sorry I didn't post the full thing before, but I think the problem is with GROUP_CONCAT, which groups the entire recordset where as I want to only group the records in the boxes table....maybe I should use a subquery?
Current output
179-1 | 2 | 2,4,3 | Canada
Expected output
168-1 | 1 | | Texas
.....
......
179-1 | 2 | 2,4,3 | Canada
.....
......

Try Converting Date
WHERE
op.order_active=0
AND op.ship_date>=
convert(datetime,cast('2013-01-01' as varchar),101)
AND op.ship_date<=
convert(datetime,cast('2013-04-24' as varchar),101)
ORDER BY op.ship_date DESC

SELECT * FROM
(
SELECT * FROM order op INNER JOIN
supplier s ON s.supplier_id = op.supplier_name
WHERE
op.order_active=0
AND op.ship_date>='2013-01-01'
AND op.ship_date<='2013-04-24'
ORDER BY op.ship_date DESC
) AS a
LEFT JOIN order_boxes opb ON a.sub_order_id = opb.sub_order_id
Try this and tell me whether this help you or not.

Related

MySQL View in place of subquery does not return the same result

The query below is grabbing some information about a category of toys and showing the most recent sale price for three levels of condition (e.g., Brand New, Used, Refurbished). The price for each sale is almost always different. One other thing - the sales table row id's are not necessarily in chronological order, e.g., a toy with a sale id of 5 could have happened later than a toy with a sale id of 10).
This query works but is not performant. It runs in a manageable amount of time, usually about 1s. However, I need to add yet another left join to include some more data, which causes the query time to balloon up to about 9s, no bueno.
Here is the working but nonperformant query:
SELECT b.brand_name, t.toy_id, t.toy_name, t.toy_number, tt.toy_type_name, cp.catalog_product_id, s.date_sold, s.condition_id, s.sold_price FROM brands AS b
LEFT JOIN toys AS t ON t.brand_id = b.brand_id
JOIN toy_types AS tt ON t.toy_type_id = tt.toy_type_id
LEFT JOIN catalog_products AS cp ON cp.toy_id = t.toy_id
LEFT JOIN toy_category AS tc ON tc.toy_category_id = t.toy_category_id
LEFT JOIN (
SELECT date_sold, sold_price, catalog_product_id, condition_id
FROM sales
WHERE invalid = 0 AND condition_id <= 3
ORDER BY date_sold DESC
) AS s ON s.catalog_product_id = cp.catalog_product_id
WHERE tc.toy_category_id = 1
GROUP BY t.toy_id, s.condition_id
ORDER BY t.toy_id ASC, s.condition_id ASC
But like I said it's slow. The sales table has about 200k rows.
What I tried to do was create the subquery as a view, e.g.,
CREATE VIEW sales_view AS
SELECT date_sold, sold_price, catalog_product_id, condition_id
FROM sales
WHERE invalid = 0 AND condition_id <= 3
ORDER BY date_sold DESC
Then replace the subquery with the view, like
SELECT b.brand_name, t.toy_id, t.toy_name, t.toy_number, tt.toy_type_name, cp.catalog_product_id, s.date_sold, s.condition_id, s.sold_price FROM brands AS b
LEFT JOIN toys AS t ON t.brand_id = b.brand_id
JOIN toy_types AS tt ON t.toy_type_id = tt.toy_type_id
LEFT JOIN catalog_products AS cp ON cp.toy_id = t.toy_id
LEFT JOIN toy_category AS tc ON tc.toy_category_id = t.toy_category_id
LEFT JOIN sales_view AS s ON s.catalog_product_id = cp.catalog_product_id
WHERE tc.toy_category_id = 1
GROUP BY t.toy_id, s.condition_id
ORDER BY t.toy_id ASC, s.condition_id ASC
Unfortunately, this change causes the query to no longer grab the most recent sale, and the sales price it returns is no longer the most recent.
Why is it that the table view doesn't return the same result as the same select as a subquery?
After reading just about every top-n-per-group stackoverflow question and blog article I could find, getting a query that actually worked was fantastic. But now that I need to extend the query one more step I'm running into performance issues. If anybody wants to sidestep the above question and offer some ways to optimize the original query, I'm all ears!
Thanks for any and all help.
The solution to the subquery performance issue was to use the answer provided here: Groupwise maximum
I thought that this approach could only be used when querying a single table, but indeed it works even when you've joined many other tables. You just have to left join the same table twice using the s.date_sold < s2.date_sold join condition and make sure the where clause looks for the null value in the second table's id column.

Use SELECT through three table

I tried to write a query, but unfortunately I didn't succeed.
I want to know how many packages delivered over a given period by a person.
So I want to know how many packages were delivered by John (user_id = 1) between 01-02-18 and 28-02-18. John drives another car (another plate_id) every day.
(orders_drivers.user_id, plates.plate_name, orders.delivery_date, orders.package_amount)
I have 3 table:
orders with plate_id delivery_date package_amount
plates with plate_id plate_name
orders_drivers with plate_id plate_date user_id
I tried some solutions but didn't get the expected result. Thanks!
Try using JOINS as shown below:
SELECT SUM(o.package_amount)
FROM orders o INNER JOIN orders_drivers od
ON o.plate_id=od.plate_id
WHERE od.user_id=<the_user_id>;
See MySQL Join Made Easy for insight.
You can also use a subquery:
SELECT SUM(o.package_amount)
FROM orders o
WHERE EXISTS (SELECT 1
FROM orders_drivers od
WHERE user_id=<user_id> AND o.plate_id=od.plate_id);
SELECT sum(orders.package_amount) AS amount
FROM orders
LEFT JOIN plates ON orders.plate_id = orders_drivers.plate_id
LEFT JOIN orders_driver ON orders.plate_id = orders_drivers.plate_id
WHERE orders.delivery_date > date1 AND orders.delivery_date < date2 AND orders_driver.user_id = userid
GROUP BY orders_drivers.user_id
But seriously, you need to ask questions that makes more sense.
sum is a function to add all values that has been grouped by GROUP BY.
LEFT JOIN connects all tables by id = id. Any other join can do this in this case, as all ids are unique (at least I hope).
WHERE, where you give the dates and user.
And GROUP BY userid, so if there are more records of the same id, they are returned as one (and summed by their pack amount.)
With the AS, your result is returned under the name 'amount',
If you want the total of packageamount by user in a period, you can use this query:
UPDATE: add a where clause on user_id, to retrieve John related data
SELECT od.user_id
, p.plate_name
, SUM(o.package_amount) AS TotalPackageAmount
FROM orders_drivers od
JOIN plates p
ON o.plate_id = od.plate_id
JOIN orders o
ON o.plate_id = od.plate_id
WHERE o.delivery_date BETWEEN convert(datetime,01/02/2018,103) AND convert(datetime,28/02/2018,103)
AND od.user_id = 1
GROUP BY od.user_id
, p.plate_name
It groups rows on user_id and plate_name, filter a period of delivery_date(s) and then calculate the sum of packageamount for the group

query to have all itms from one table but order by another table that doesn't have all items

I have one table products with id of product and name.
Second table is products_last_usage where I keep product_id, user_id and last_used_at.
Whenever a user clicks on a product, I have the field last_used_at updated.
Now, I need a query to list all products, and order them first by last_used_at, and then by name of product. It has to be PER USER. i.e. every user will have his own order of the table.
But I need all products, even if there are no records of them in the second table.
How to do that?
You can help me with a rails query or mysql query.
You can use a left join:
select p.*
from products p left join
products_last_usage plu
on plu.product_id = p.id and plu.user_id = $user_id
order by (last_used_at is not null) desc, last_used_at desc;
Ordering by last_used_at desc should also work. However, I think it is clearer to explicitly handle NULL values.
I think you can start from something like this. Pls next time post sample data, expected results etc.
SELECT A.PRODUCT_ID
, A.PRODUCT_NAME
, B.USER_ID
, B.LAST_USED_AT
FROM PRODUCTS A
LEFT JOIN PRODUCTS_LAST_USAGE B ON A.PRODUCT_ID = B.PRODUCT_ID
ORDER BY B.USER_ID, B.LAST_USED_AT DESC, A.PRODUCT_NAME;

Listing top 5 users measured by most common rows in foreign key table

Here's a picture of my database structure:
When I input an Observation, I'm entering a row into Observations but also 0, 1 or more rows into Criteria.
I'm trying to write an SQL statement which allows me to select the Strongest 5 teachers in a specific criteria.
So for example I'd click on Questioning (which might have an ID of 5 in Criteria_Labels), I'd want to return a list of 5 teachers (Teacher_ID from Observations) who have the most rows of Criteria_ID = 5 in Criteria.
The statement that I've attempted to write is as follows:
SELECT t.Name AS Teacher_Name
FROM observations o
LEFT JOIN teachers t ON o.Teacher_ID = t.Teacher_ID
LEFT JOIN criteria c ON o.ID = c.Observation_ID
WHERE c.Criteria_ID = 5
ORDER BY COUNT(c.Criteria_ID) DESC
LIMIT 0,5
However, it only appears to return one member of staff. I'm not sure I've got this right at all, but hopefully I'm along the right lines.
Can anyone help? Thanks in advance,
SELECT t.Teacher_ID, t.Name AS Teacher_Name, count(*) as total
FROM observations o
LEFT JOIN teachers t ON o.Teacher_ID = t.Teacher_ID
LEFT JOIN criteria c ON o.ID = c.Observation_ID
WHERE c.Criteria_ID = 5
group by t.Teacher_ID, t.Name
order by total desc
limit 5

SQL: After joined table query, put rows with data a both tables first

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