MySQL multiple left join group by - mysql

There is something wrong with this MySQL code.
it seems to be returning more stock then there should be.
table positions holds the stock available (multiple positions one product)
table orderbody holds the orders ordered products (1 orderheader to many orderbody)
SELECT PRO.ProductID,
PRO.ProductCode,
SUM( POS.Qty ) AS instock,
SUM( OB.Qty ) AS onorder
FROM products AS PRO
LEFT JOIN position AS POS ON POS.ProductID = PRO.ProductID
LEFT JOIN orderbody AS OB ON OB.ProductID = PRO.ProductID
WHERE POS.ProductID = OB.ProductID
GROUP BY PRO.ProductID, POS.ProductID, OB.ProductID
i'm getting instock 320
actual stock quantity = 40
number of positions = 2 (qty 20 each)
onorder = 16 qty
actual number of orderbody = 8 rows
actually on order = 8 (each with qty = 1)
this is on one of the products
i know it has something to do with the group by but i cant work it out.
Appreciate any help received.

I had the same problem a few days ago. Try it by SELECTing from a separate query: SELECT ... FROM products, (SELECT...)..." where you have the two tables to be left joined. try to test the sub-query by itself first, and try to put it together once ot works. (once you have the data you want, and not duplicates, because that is you problem.

you are selecting this field PRO.ProductCode, but not grouping by it, at a guess it might be the problem.

Related

I would like to know if there is a better way to write this query (multiple joins of the same table)

here is the problem:
I have vehicles table in db (fields of this table are not so important), what's important is that each vehicle has a model_id, which refers to the vehicle_models table.
Vehicle models table has id, class, model, series, cm3hp, created_at and updated_at fields.
I need to define the stock age in terms of how many vehicles of the certain model class are on the stock by the given criteria. The criteria being: 0-30 days, 31-60 days, 61-90 days... 360 + days...
I don't know if it is clear enough but let me try to explain even better: For each day range I need to find the count of vehicles with the given model class. There are other criteria but that's not important for what I am trying to find out. To help you better understand the problem I'll include the screenshot of how the structure should look like:
I am using MySQL 8.
The query I wrote is:
SELECT DISTINCT vm.class,
IFNULL(t1.count, 0) as t1c,
IFNULL(t2.count, 0) as t2c,
IFNULL(t3.count, 0) as t3c,
IFNULL(t4.count, 0) as t4c,
IFNULL(t5.count, 0) as t5c,
IFNULL(t6.count, 0) as t6c,
IFNULL(t7.count, 0) as t7c
FROM vehicle_models vm
LEFT JOIN (
SELECT
vm.class as class,
count(*) as count
FROM a3s186jg7ffmm0q8.vehicles v
JOIN vehicle_models vm
ON vm.id = v.model_id
WHERE
DATEDIFF(IFNULL(v.retail_date, now()), v.wholesale_date) BETWEEN 0 AND 30
GROUP BY vm.class
) t1 ON t1.class = vm.class
*** MORE SAME LEFT JOINS ***
ORDER BY vm.class;
Now, this provides desired results, but what I would like to know if there is a better way to write this query in terms of performance and also code structure.
I guesss you are presenting a report of inventory aging (of how long that car sits on the dealer's lot before somebody buys it). You can put the age ranges in your top-level select rather than putting each one in a separate subquery. That will make your query faster (subqueries have a cost) and shorter / easier to read.
Try something like this nested query. The inner query gives back one row per vehicle with its aging number. The outer query aggregates them.
SELECT class,
COUNT(*) total,
SUM(age BETWEEN 0 AND 30) t1c,
SUM(age BETWEEN 31 AND 60) t2c,
SUM(age BETWEEN 61 AND 90) t3c,
... etc ...
FROM (
SELECT vm.class,
DATEDIFF(IFNULL(v.retail_date, now()), v.wholesale_date) age
FROM a3s186jg7ffmm0q8.vehicles v
JOIN vehicle_models vm ON vm.id = v.model_id
) subq
GROUP BY class
ORDER BY class;
This SUM() trick works in MySQL because expressions like age BETWEEN 0 AND 30 have the value 1 when true and 0 when false.

MySQL Left Join throwing off my count numbers

I'm doing a left join on a table to get the number of leads we've generated today and how many times we've called those leads. I figured a left join would be the best thing to do, so I wrote the following query:
SELECT
COUNT(rad.phone_number) as lead_number, rals.lead_source_name as source, COUNT(racl.phone_number) as calls, SUM(case when racl.contacted = 1 then 1 else 0 end) as contacted
FROM reporting_app_data rad
LEFT JOIN reporting_app_call_logs racl ON rad.phone_number = racl.phone_number, reporting_app_lead_sources rals
WHERE DATE(rad.created_at) = CURDATE() AND rals.list_id = rad.lead_source
GROUP BY rad.lead_source;
But the problem with that, is that if in the reporting_app_call_logs table, there are multiple entries for the same phone number (so a phone number has been called multiple times that day), the lead_number (which I want to count how many leads were generated on the current day grouped by lead_source) equals how many calls there are. So the count from the LEFT table equals the count from the RIGHT table.
How do I write a SQL query that gets the number of leads and the total number of calls per lead source?
Try COUNT(DISTINCT expression)
In other words, change COUNT(rad.phone_number) to COUNT(DISTINCT rad.phone_number)

SQL Script for Counting Records based on specific criteria and returning the Counts

I'm Trying to count records with specific criteria in my Database to ultimately produce some statistical reports.
My Tables and Fields are:
1- Import Table:bed_ID, unit_ID, mrn, acccount_num, sex, service_ID
2- Beds: bed_ID, unit_ID, bed_type_ID
3- Bed_Type: bed_type_ID, bed_type_description
4- Unit: unit_ID, unit_common_name, program_ID, total_beds...other fields that don't apply.
5- Service: service_ID, service_common_name, program_ID
6- Program: program_ID, program_common_name
I want to create a query that will give me a count of each Bed_Type_Description for each Unit. I also want to get each units total beds and calculate beds available but I'm sure I can figure that out if I get help with this part.
Unit Regular_Bed Escalation_Bed Transfer_Bed Bassinet
-------------------------------------------------------------
Unit1 10 4 2 2
Unit2 12 2 2 0
etc...
etc...
This is what I have, but its only related to one specific Unit:
SELECT
COUNT(dw_test.dbo.Bed_Type.bed_type_description) as 'Total Number of Beds'
FROM
dw_test.dbo.MediTechReport_Bed_Board,
--dw_test.dbo.Unit,
dw_test.dbo.Beds,
dw_test.dbo.Bed_Type
WHERE
dw_test.dbo.MediTechReport_Bed_Board.bed_ID = dw_test.dbo.Beds.bed_ID
--AND
-- dw_test.dbo.MediTechReport_Bed_Board.unit_ID = dw_test.dbo.Unit.unit_ID
AND
dw_test.dbo.Beds.bed_type_ID = dw_test.dbo.Bed_Type.bed_type_ID
AND
dw_test.dbo.MediTechReport_Bed_Board.unit_ID = 'KA2MED'
AND
dw_test.dbo.Bed_Type.bed_type_description = 'Regular';
You'll notice a couple lines related to Unit that are commented out. With these commented out I get a returned value of '5' records which is correct. If i remove the commenting to include these lines it returns a value of '0' which makes no sense to me. If someone can explain this to me that'd be great as well.
My SQL is quite rusty, its been a while. Any assistance is greatly appreciated. Thanks again in advance for all your help.
Maybe something like this?
select t.bed_type_description, COUNT(t.bed_type_description) as 'Total Number Of Beds'
from import As I inner join Beds as b on i.bed_Id = b.bed_Id
inner join Unit as u on i.unit_Id = u.unit_Id
inner join Bed_Type as t on b.bed_type_Id = t.bed_type_Id
where u.unit_Id = 'KA2MED'
group by t.bed_type_description

SQL find rows in the same table based on certain columns

I have two tables which are for two different programs. Each program has a specific program_instance (the program_instance) is the year of the program.
One table is called 'enrollees' and the other is 'nominations' - for two programs that aren't technically related.
I've been trying to get the count of past participants from both tables. For reference:
program_instance_id:
5 = GC 2014
3 = GC 2013
1 = GC 2012
4 = GE 2013
2 = GE 2012
So I ran this query on my enrollees table and it produced a result in 913ms:
SELECT count(*) AS prev_enrollees
FROM outreach.enrollees e1
WHERE e1.program_instance_id = 5 AND EXISTS
(SELECT * FROM outreach.enrollees e2
WHERE e1.first_name = e2.first_name
AND e1.last_name = e2.last_name
AND e1.address1 = e2.address1
AND e1.state = e2.state
AND e1.zip = e2.zip
AND e2.program_instance_id < 5);
This query, to my understanding, would give me the number of rows in the 'enrollees' table where an enrollee from the current year (program_instance_id = 5) had previous enrolled in another year. The result it produces is pretty accurate, to my understanding.
So... I ran this EXACT query (changing the table name) on my 'nominations' table. The nominations table has almost the exact structure of the 'enrollees' table (some columns are different, but the person's information fields are identical). This query ran for over a half hour before I cancelled it. It's not popping out an almost-instant result like it was on the enrollee table and I don't know why it would take longer.
I could imagine if there were a lot more rows in the table but the enrollee table has about 50k MORE rows than the nominations table.
I've also tried:
SELECT count(*) AS prev_enrollees
FROM outreach_grow_education.nominations e1
JOIN outreach_grow_education.nominations e2 ON e1.first_name = e2.first_name
AND e1.last_name = e2.last_name
AND e1.address1 = e2.address1
AND e1.state = e2.state
AND e1.zip = e2.zip
AND 4 = e2.program_instance_id
WHERE e1.id IS NOT NULL AND e1.program_instance_id = 2;
Alas, to the same result. Instant result on enrollees, never-ending on nominations.
Is there any other alternative for what I'm trying to achieve that wouldn't cause the never-ending cycle?
I suggest checking the indexes of the two tables, specifically for the columns you use in the JOIN clauses: first_name, last_name, address1, state, zip, and program_instance_id. Chances are one or more of these columns is indexed in the "enrollees" table and not in "nominations."

Mysql: Adding product restricted shipping options to cart

I have a custom shop, and I need to redo the shipping. However, that is sometimes later, and in the meantime, I need to add a shipping option for when a cart only contains a certain range of products.
SO there is a ship_method table
id menuname name zone maxweight
1 UK Standard ukfirst 1 2000
2 UK Economy uksecond 1 750
3 Worldwide Air world_air 4 2000
To this I have added another column prod_restrict which is 0 for the existing ones, and 1 for the restricted ones, and a new table called ship_prod_restrict which contains two columns, ship_method_id and item_id, listing what products are allowed in a shipping category.
So all I need to do is look in my transactions, and for each cart, just check which shipping methods are either prod_restrict of 0 or have 1 and have no products in the cart that aren't in the restriction table.
Unfortunately it seems that because you can't values from an outer query to an inner one, I can't find a neat way of doing it. (edited to show the full query due to comments below)
select ship_method.* from ship_method, ship_prod_restrict where
ship_method.`zone` = 1 and prod_restrict='0' or
(
prod_restrict='1'
and ship_method.id = ship_prod_restrict.ship_method_id
and (
select count(*) from (
select transactions.item from transactions
LEFT JOIN ship_prod_restrict
on ship_prod_restrict.item_id = transactions.item
and ship_prod_restrict.ship_method_id=XXXXX
where transactions.session='shoppingcartsessionid'
and item_id is null
) as non_permitted_items < 1 )
group by ship_method.id
gives you a list of whether the section matches or not, and works as an inner query but I can't get that ship_method_id in there (at XXXXX).
Is there a simple way of doing this, or am I going about it the wrong way? I can't currently change the primary shipping table, as this is already in place for now, but the other bits can change. I could also do it within PHP but you know, that seems like cheating!
Not sure how the count is important, but this might be a bit lighter - hard to tell without a full table schema dump:
SELECT COUNT(t.item) FROM transactions t
INNER JOIN ship_prod_restrict r
ON r.item_id = t.item
WHERE t.session = 'foo'
AND r.ship_method_id IN (**restricted, id's, here**)