I need to perform a COUNT on a quite a big query, where one of the joined tables has a one-to-many relationship. This is throwing off my result as all data is being multiplied by the number of times an item is repeated in the 'many' side of the one-to-many table.
This is a shortened version of the query showing only the relevant portion to highlight the issue:
SELECT COUNT(trimtype) FROM versiontrim
INNER JOIN trims USING (trim_id)
INNER JOIN prices USING(version_id)
INNER JOIN m_versions USING(version_id)
WHERE trimtype IN('sec', 'help') AND price BETWEEN 200001 AND 210000
GROUP BY version_id
All tables are quite straighforward except m_versions that has the one-to-many relationship and looks like this:
version_id serv_id
1 1
1 2
1 3
1 4
1 5
.... and so on
The expected result of the query is :
version_id COUNT(trimtype)
44 9
54 7
69 9
214 10
216 6
282 1
290 10
Instead I am getting this,ie, all counts multiplied by 5 which is the number of times version_id is repeated in the m_versions table:
version_id COUNT(trimtype)
44 45
54 35
69 45
214 50
216 30
282 5
290 50
How to avoid this behavior?
Thanks
It matches to multiple records on table m_version that is why you are getting invalid result. Try wrapping it a subquery,
INNER JOIN (SELECT DISTINCT version_id FROM m_versions) m USING(version_id)
UPDATE
So the full query will look like this,
SELECT version_id, COUNT(trimtype)
FROM versiontrim
INNER JOIN trims USING (trim_id)
INNER JOIN prices USING(version_id)
INNER JOIN (SELECT DISTINCT version_id FROM m_versions) m USING(version_id)
WHERE trimtype IN('sec', 'help') AND price BETWEEN 200001 AND 210000
GROUP BY version_id
Related
I have two tables, customer and customer_order
customer
cust_id, cust_name
121 Acme Wholesalers
234 Griffen Electric
336 East Coast Marine Supplies
544 Sanford Automotive
customer_orders
order_num,cust_id,order_date
1 121 2019-01-15
2 234 2019-07-24
3 336 2020-05-02
4 121 2019-01-15
5 336 2020-03-19
6 234 2019-07-24
7 121 2019-01-15
8 336 2020-06-12
I need to find out the name of each customer who has placed exactly 3 orders but using a non-correlated subquery and without JOINS
I have got the same query results using correlated query
SELECT c.cust_name FROM customer c WHERE cust_id
IN (SELECT GROUP_CONCAT(cust_id) FROM customer_order )
but this time I need same results using non-correlated and without join also
seems like the big rock is finding cust_id values that have exactly three occurrences in customer_order we can do something like this
SELECT o.cust_id
FROM customer_orders o
GROUP
BY o.cust_id
HAVING COUNT(*) = 3
referencing the aggregate in a HAVING clause to restrict rows are returned.
we could use this query as an inline view (or, derived table, in the the mysql parlance), and then
SELECT n.cust_id
FROM (
-- cust_id that have exactly three rows
SELECT o.cust_id
FROM customer_orders o
GROUP
BY o.cust_id
HAVING COUNT(*) = 3
) n
and typically do a JOIN to the customers table to retrieve the name.
given a weird restriction of "no joins", we could (less optimally) use an WHERE foo IN (subquery) form
I have 2 tables that contains product sets IDs containing product IDs. Nid is product set ID and second row contains products attached to the set. The reason why 2 tables is that first table contains paid products, second table contains free products. Based on existing products in cart, I created a query to get results like this:
1 product in cart having ID = 45
result:
nid
field_prod_in_set_nid
field_additional_set_prod_nid
67
45,45
45
query: ($items_string is variable containing product IDs)
SELECT i.nid, GROUP_CONCAT(i.field_prod_in_set_nid SEPARATOR ',') as set_products, ad.additional
FROM {content_field_prod_in_set} i
JOIN (
SELECT c.nid, GROUP_CONCAT(c.field_additional_set_prod_nid SEPARATOR ',') as additional
FROM
{content_field_additional_set_prod} c
GROUP BY c.nid) ad ON ad.nid = i.nid
WHERE
i.nid IN (SELECT nid FROM {content_field_prod_in_set} WHERE field_prod_in_set_nid IN ($items_string))
GROUP BY i.nid
content_field_prod_in_set
nid
field_prod_in_set_nid
62
3
62
3
64
3
63
15
64
25
62
29
67
45
67
45
content_field_additional_set_prod
nid
field_additional_set_prod_nid
62
46
62
9
63
NULL
64
46
67
45
QUESTION :
Is it possible to write the query without having select in WHERE ? I read that this is not a good practice and it is slow. Thank you.
Join with the subquery instead of using WHERE IN.
SELECT i.nid, GROUP_CONCAT(i.field_prod_in_set_nid SEPARATOR ',') as set_products, ad.additional
FROM content_field_prod_in_set i
JOIN (
SELECT c.nid, GROUP_CONCAT(c.field_additional_set_prod_nid SEPARATOR ',') as additional
FROM
content_field_additional_set_prod c
GROUP BY c.nid) ad ON ad.nid = i.nid
JOIN (SELECT DISTINCT nid
FROM content_field_prod_in_set
WHERE field_prod_in_set_nid IN ($items_string)
) i2 ON i2.nid = i.nid
GROUP BY i.nid
DEMO
SELECT DISTINCT is needed in the subquery to avoid duplicating the results in the GROUP_CONCAT() for each matching nid.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I have exhausted all of my options. I cannot understand where I am going wrong with this. Please help me. I can not take much more headbanging. Here is where I am at...
Below is an explanation of my table structure.
locations_management table
contains data linking the locations_management_id, season_id, location_id and other unneeded information together
orders table
contains data about the order including the location_management_id and the order_id as well as other unneeded information
orders_products table
product data linked to the orders by order_id. This table only uses these columns: order_product_id, order_id, product_id, piece_qty
orders_adjustments table
used to track any adjustments to the inv_shipped. This table uses the order_id, product_id, piece_qty columns
Here is where I am at today. The query below pulls data from the tables above.
Basically I am asking for the location_management_id(s) from the locations_management table WHERE season_id = 12 AND location_id = 35. There can be more than one possible location_management_id that fits both the season_id and location_id. I then need to find the orders that match these location_management_id(s). Once the orders are found, I need to use the order_id(s) to find the products associated to them in the orders_products table.
This query does exactly that but when I take it a step further to combine/SUM the
piece_qty for a total inv_shipped, crazy things happen to the numbers.
SELECT
locations_management.season_id,
locations_management.location_id,
orders.order_id,
orders_products.product_id,
IFNULL((orders_products.piece_qty), 0) AS inv_shipped,
IFNULL((orders_adjustments.piece_qty), 0) AS inv_adjustments
FROM
locations_management
JOIN orders USING (location_management_id)
LEFT JOIN orders_products USING (order_id)
LEFT JOIN orders_adjustments ON (orders_adjustments.order_id = orders_products.order_id) AND (orders_adjustments.product_id = orders_products.product_id)
WHERE
locations_management.season_id = 12 AND locations_management.location_id = 35
GROUP BY
product_id, orders_products.order_id
When I run the query above, this is what I get...
season_id location_id order_id product_id inv_shipped inv_adjustments
12 35 2127 1 220 0
12 35 2194 1 160 0
12 35 2127 3 312 0
12 35 2127 4 24 0
12 35 2127 5 180 0
12 35 2194 5 24 0
12 35 2127 7 144 0
12 35 2127 7 24 0
This is exactly what I would expect to get. Multiple order_id's grouped by the product_id and all the data is accurate. So now here becomes the problem. I want to add/SUM the product_id's together when they match and have a combined inv_shipped. So product_id 1 would now total 380 for inv_shipped.
When I take the same query from above and I add SUM to the inv_shipped and inv_adjustments (as seen below), I get this data output below. Notice how some of the values have doubled but also the matching product_id rows are not combined.
IFNULL(SUM(orders_products.piece_qty), 0) AS inv_shipped,
IFNULL(SUM(orders_adjustments.piece_qty), 0) AS inv_adjustments
season_id location_id order_id product_id inv_shipped inv_adjustments
12 35 2127 1 440 0
12 35 2194 1 160 0
12 35 2127 3 624 0
12 35 2127 4 48 0
12 35 2127 5 360 0
12 35 2194 5 24 0
12 35 2127 7 288 0
12 35 2127 7 24 0
If I change the GROUP BY to product_id only, I get the follow data:
GROUP BY product_id
season_id location_id order_id product_id inv_shipped inv_adjustments
12 35 2127 1 600 0
12 35 2127 3 624 0
12 35 2127 4 48 0
12 35 2127 5 384 0
12 35 2127 7 312 0
Again these inv_shipped totals are not correct. So where am I going wrong?
------------------------------------ Suggestions ------------------------------------
This query below was suggested but the data output for the inv_shipped is not added correctly either.
SELECT
locations_management.season_id,
locations_management.location_id,
orders.order_id,
products.product_id,
products.inv_shipped
FROM
locations_management
JOIN (SELECT location_management_id, order_id FROM orders group by order_id) AS orders ON orders.location_management_id = locations_management.location_management_id
JOIN (SELECT order_id, product_id, IFNULL(SUM(piece_qty), 0) AS inv_shipped FROM orders_products GROUP BY order_id, product_id) AS products ON products.order_id = orders.order_id
WHERE
locations_management.season_id = 12 AND locations_management.location_id = 35
ORDER BY
product_id, order_id
season_id location_id order_id product_id inv_shipped inv_adjustments
12 35 2127 1 440 0
12 35 2194 1 160 0
12 35 2127 3 624 0
12 35 2127 4 48 0
12 35 2127 5 360 0
12 35 2194 5 24 0
12 35 2127 7 288 0
12 35 2127 7 24 0
First, find the root of the issue. What changes in between the correct and incorrect information?
Let's take a look at your second query. From what I can see, there are three things that changes:
You've joined with another sub-query.
You've added a SUM operation.
You've added a GROUP BY.
Nest step is to try with removing SUM and GROUP BY, like this:
SELECT
locations_management.season_id AS season_id,
locations_management.location_id AS location_id,
orders.order_id AS order_id,
products.product_id AS product_id,
products.piece_qty
FROM
locations_management
JOIN (SELECT location_management_id, order_id FROM orders group by order_id) AS orders ON orders.location_management_id = locations_management.location_management_id
JOIN (SELECT order_id, product_id, piece_qty FROM orders_products) AS products ON products.order_id = orders.order_id
WHERE
locations_management.season_id = 12 AND locations_management.location_id = 35
I assume that each product_id will return two (or more) rows. That's probably because of your second JOIN have two (or more) rows for order_id in orders_products table; it seems obvious because the first sub-query for table orders have group by order_id. So, now to quickly fix this, you need to do the SUM inside the second sub-query instead. Something like this:
SELECT
locations_management.season_id AS season_id,
locations_management.location_id AS location_id,
orders.order_id AS order_id,
products.product_id AS product_id,
products.inv_shipped
FROM
locations_management
JOIN (SELECT location_management_id, order_id FROM orders group by order_id) AS orders ON orders.location_management_id = locations_management.location_management_id
JOIN (SELECT order_id, product_id, IFNULL(SUM(products.piece_qty), 0) AS inv_shipped FROM orders_products GROUP BY order_id, product_id) AS products ON products.order_id = orders.order_id
WHERE
locations_management.season_id = 12 AND locations_management.location_id = 35;
This might return you the correct result however I personally will write the query like this:
SELECT lm.season_id, lm.location_id, o.order_id , p.product_id, p.inv_shipped
FROM locations_management AS lm
JOIN (SELECT location_management_id, order_id
FROM orders
GROUP BY location_management_id,order_id) AS o
ON o.location_management_id = lm.location_management_id
JOIN (SELECT order_id, product_id, IFNULL(SUM(products.piece_qty), 0) AS inv_shipped
FROM orders_products
GROUP BY order_id, product_id) AS p
ON p.order_id = o.order_id
WHERE
lm.season_id = 12 AND lm.location_id = 35;
You don't need to set alias if your alias is the same as column name; for example lm.season_id AS season_id. If you remove .. AS season_id, the column will be recognized as season_id nonetheless. You won't see it as lm.season_id.. well at least for most tools that I know of. Also, I personally think aliases are meant to shorten long table or column names but "to each their own".
GROUP BY should include all the non-aggregated column(s) in SELECT. Of course, if the sql_mode=only_full_group_by is turned off, you can run the query but the correct setting should be ON. You can read more of the reason why here.
With the additional columns added in GROUP BY, this query might not return the result you once had. That's depending on you data and if that happen, I suggest you edit your question and add a Minimal, reproducible example. At the moment, we're only seeing queries and no example table/data to work with. Its better if you can create a fiddle with a few rows of data.
I don't see the need for those nested subqueries. And if you want one row per order and product, then aggregation may not be necessary.
You seem to want Doesn't this do what you want?
select lm.season_id, lm.location_id,
op.order_id, op.product_id, op.piece_qty as inv_shipped
from locations_management lm
inner join orders o on o.location_management_id = lm.location_management_id
inner join order_products op on op.order_id = o.order_id
where lm.season_id = 12 and lm.location_id = 35
Or if you want one row per product:
select lm.season_id, lm.location_id,
op.product_id, coalesce(sum(op.piece_qty), 0) as inv_shipped
from locations_management lm
inner join orders o on o.location_management_id = lm.location_management_id
left join order_products op on op.order_id = o.order_id
where lm.season_id = 12 and lm.location_id = 35
group by lm.season_id, lm.location_id, op.product_id
table:tab1
id date_time zoneid accountid slotid trequest bidder width height
_50832 2017-09-04 15:41:06 153 1654 153x468x60 10 aaa 468 60
_50832 2017-09-04 15:41:06 152 1654 152x468x60 10 bbb 468 60
table:tab2
id date_time zoneid accountid slotid bidder count
_50832 2017-09-04 15:41:06 152 1654 152x468x60 bbb 6
_50832 2017-09-04 15:41:06 152 1654 152x468x60 bbb 4
_50832 2017-09-04 15:41:06 153 1654 153x468x60 aaa 9
_50832 2017-09-04 15:41:06 153 1654 153x468x60 aaa 1
below is my query:
SELECT SUM(req.trequest) as REQ, SUM(win.count) as IMP
FROM tab1 as req
JOIN tab2 as win ON (req.id=win.id AND req.zoneid=win.zoneid)
GROUP BY req.zoneid
I get below result,
REQ IMP
20 10
20 10
IMP count is correct but I get wrong REQ count. My expected result is
REQ IMP
10 10
10 10
How to get my expected result?
Lets find the sum of trequest and count separately based on zoneid and id.Then use these two results ( t1 and t2 ) in the inner join.
Count mismatch problem shown in the question occur due to multiple rows satisfying the joining conditions.
In this solution we will only have one entry for each zoneid in both the results ( t1 and t2 ). So the problem is avoided.
Note: You can remove the id column from the GROUP BY clause if it doesn't make any difference.
SELECT t1.id, t1.zoneid, t1.REQ, t2.IMP FROM
(SELECT id,zoneid,SUM(trequest) as REQ
FROM tab1 GROUP BY zoneid,id ) t1
INNER JOIN
(SELECT id,zoneid SUM(win.count) as IMP
FROM tab2 GROUP BY zoneid,id ) t2
ON t1.id = t2.id
AND t1.zoneid = t2.zoneid
Let's try first sumwin.count and group records in sub-query, after it join tables. Try in following:
SELECT SUM(req.trequest) as REQ, SUM(win.count) as IMP
FROM tab1 as req
JOIN (
SELECT SUM(win.count) as IMP, win.zoneid, win.id
FROM tab2 as win
GROUP BY win.zoneid, win.id) AS win ON req.id=win.id AND req.zoneid=win.zoneid
GROUP BY req.zoneid
Instead of req.zoneid. You should try win.zoneid. What seems is that the rows in table 1 are counted multiple times as zoneid in table 2 comes twice. So win.zoneid would group it and avoid the repetition.
Updated: The solution posted by #mayur panchal is the correct one as you don't need to SUM the rows in first table as they belong to different zoneid. If you SUM them you will obviously get the 20 repeated twice.
I was successful in writing the query that lists salesmen that did sell to a particular customer, but not those that have not. I suspect it is because the same salesmen that sold to the specific customer, also sold to other customers.
select a.name from salesperson a inner join orders b on
a.salesperson_id = b.salesperson_id where cust_id="4";
I was thinking that modifying the same query like this would do the trick:
.... a.salesperson_id <> b.salesperson_id where cust_id="4";
But the result lists all the salesmen. This is most likely due to the fact that the same salesmen that were returned in the original query, also sold to other customers
The 3 tables look like this:
Salesperson table
salesperson_ID, Name, Age, Salary
1 Abe 61 140000
2 Bob 34 44000
5 Chris 34 40000
7 Dan 41 52000
8 Ken 57 115000
11 Joe 38 38000
Customer table
cust_ID, Name, City Industry Type
4 faralon sacramento H
6 Apple cupertino S
7 Honda NY B
9 Kolb Oshkosh B
Orders table
Number, Order_date, cust_id, salesperson_id, Amount
10 8/2/1996 4 2 540
20 1/30/1999 4 8 1800
30 7/14/1995 9 1 460
40 1/29/1998 7 2 2400
50 2/3/1998 6 7 600
60 3/2/1998 6 7 720
70 5/6/1998 9 7 150
Any help would be greatly appreciated. ~Alpinehyker
You can do something like this:
select a.name from salesperson a
left join orders b on a.salesperson_id = b.salesperson_id and b.cust_id="4"
where b.Number is null
So, get all salepersons, left join to orders for customer 4, and return only rows where there is no such order.
I am assuming that Number is the primary key for Orders, or at least not null.
All salespeople who have NOT sold to customer_ID 4:
SELECT s.Name FROM Salesperson AS s
LEFT JOIN Orders AS o
ON s.salesperson_ID = o.salesperson_ID
WHERE o.customer_ID <> 4
GROUP BY o.salesperson_ID;
Perhaps this will work
SELECT
s.*
FROM `Salesperson` AS s
LEFT JOIN `Orders` AS o ON o.`salesperson_id` = s.`salesperson_ID`
WHERE
o.`cust_id` NOT IN (4)
GROUP BY s.`salesperson_ID`;
Answer to your 2nd question:
SELECT
COUNT(*) AS num_of_orders
,s.`Name`
FROM `Salesperson` AS s
LEFT JOIN `Orders` AS o ON o.`salesperson_id` = s.`salesperson_ID`
GROUP BY s.`salesperson_ID`
HAVING num_of_orders >= 2;
...and 3rd question. (assuming you have your highAchiever table ready)
INSERT INTO `highAchiever`
(`Name`,`Age`)
SELECT
`Name`
,`Age`
FROM `Salesperson`
WHERE
`Salary` >= 100000;