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.
Related
I am trying to get the records where avg is greater than 81, I noticed I can't use a simple where avg(score) > 80
But using a Having statement is problematic as well as it does not consider where the individual records average is greater than 80, but it considers the group average. Is there an alternative?
In general, if we want to return aggregates (SUM,AVG) and also return detail that makes up the aggregate, we typically use two SELECT
As a rudimentary example, consider a table of "test_score"
test_id student_id score
------- ---------- -----
101 6 90
101 7 71
101 8 88
222 6 93
222 7 78
222 8 81
We can calculate the average score for each test, with a SELECT ... GROUP BY query.
SELECT r.test_id AS test_id
, AVG(r.score) AS avg_score
, MAX(r.score) AS high_score
FROM test_score r
GROUP
BY r.test_id
We expect that to return a resultset like this:
test_id avg_score
------- ---------
101 83
222 84
We can use that query as an inline view i.e. we wrap it in parens and reference it like a table in the FROM clause of another SELECT.
As a demonstration, to return student scores that were better (or equal to) average for each test:
SELECT s.test_id
, s.avg_score
, t.student_id
, t.score
FROM ( -- inline view to get average score for each test_id
SELECT r.test_id AS test_id
, AVG(r.score) AS avg_score
FROM test_score r
GROUP
BY r.test_id
) s
LEFT
JOIN test_score t
ON t.test_id = s.test_id
AND t.score >= s.avg_score
ORDER
BY t.test_id
, s.score DESC
And we'd expect that to return something like:
test_id avg_score student_id score
------- --------- ---------- -----
101 83 6 90
101 83 8 88
222 84 6 93
The first two columns, returned from the inline view, are the result of the aggregate (AVG). The last two columns are detail rows, matched to the rows from the aggregate result.
To summarize the main point here:
To return aggregates along with details, we typically need two SELECT.
One SELECT to get the aggregates (with a GROUP BY if the aggregates are "per" each something or other)
Another SELECT to get the details and a match to the aggregate.
If the average score being computed in your query is already correct, you are just having trouble filtering by it, just wrap it in parens and select from it
select * from (
SELECT Count(entry_id) AS Filled,
q.question AS Questions,
AVG(ag.score) AS TOTAL
FROM entry e
LEFT JOIN entry_answer ea
ON ea.entry_id= e.entry
LEFT JOIN question q
ON q.question_id = ea.question_id
LEFT JOIN question_group qg
ON ea.question_parent_id = qg.question_parent_id
LEFT JOIN answer_group ag
ON ag.question_id = qg.question_parent_id
JOIN sent_list using (sent_list_id)
WHERE
entry_group_id = 2427
AND ag.score >= 0
AND ea.rated_answer_id = ag.rated_answer_id
AND sent_id = 6156
AND e.entry_date BETWEEN '2018-01-01' AND '2019-12-31'
group by ea.question_id
) results where total >= 81
I am new with mysql and working to change a store application to make it have two stock. I created a table to store stock quantity:
Then I plan to create a view with stock quantity, per store, per SKU. I using the following query:
SELECT
`stockList`.`sku`,
SUM(A.`stockQty`) AS 'store1',
SUM(B.`stockQty`) AS 'store2',
SUM(`stockList`.`stockQty`) AS 'total'
FROM `stockList`
LEFT JOIN (
SELECT * FROM `stockList` WHERE `idStock`=1
) AS A
ON `stockList`.`sku`=A.`sku`
LEFT JOIN (
SELECT * FROM `stockList` WHERE `idStock`=2
) AS B
ON `stockList`.`sku`=B.`sku`
GROUP BY `stockList`.`sku`
Per resulting table, calculation is not proper and I could not identify the logic:
SKU 43 should show for store1 = 9 and for store2 = 10, total = 19. This is what they show if I execute the select queries alone. Please, let me know if I misunderstood how this sum logic works.
You might to use SUM on subquery to calculate Totle price by sku
LEFT JOIN may make some fields not match causing NULL so use IFNULL to preset value 0
You can try this.
SELECT
T.sku,
SUM(T.stockQty) as totle,
IFNULL(A.`store1`,0) AS `store1`,
IFNULL(B.`store2`,0) AS `store2`
FROM `stockList` AS T
LEFT JOIN
(
SELECT sku,SUM(`stockQty`) as `store1`
FROM `stockList`
WHERE `idStock`=1
GROUP BY sku
) as A ON A.sku = T.sku
LEFT JOIN
(
SELECT sku,SUM(`stockQty`) as `store2`
FROM `stockList`
WHERE `idStock`=2
GROUP BY sku
) AS B ON T.sku =B.sku
GROUP BY T.sku
sqlfiddle
Your query is much more complicated than it needs to be. You can just do this:
SELECT
sku,
SUM(stockQty) as total,
SUM(IF(idStock=1,stockQty,0)) AS `store1`,
SUM(IF(idStock=2,stockQty,0)) AS `store2`
FROM `stockList`
GROUP BY sku
Output:
sku total store1 store2
36 10 10 0
37 3 3 0
38 4 4 0
39 3 3 0
40 10 10 0
41 12 12 0
42 12 12 0
43 19 9 10
I'm wondering what is the right way to display all id_product if is NOT repeated 4 time.
This is my table:
id_product id_related
55 1
55 2
55 3
55 4
11 1
11 123
11 12
36 12
36 9
36 14
36 654
I need to find products without added 4 related products.
In this case the result i expect is 11.
Something like that
select id_product, count(*)
from <table>
group by id_product
having count(*) < 4
Following query
SELECT id_product
FROM table
GROUP BY id_product
HAVING COUNT(id_product) < 4
Select
p.id_product,
p.id_related
from product p
join
(
select
id_product,
count(id_related) as tot
from product
group by id_product
having tot <> 4
)p1
on p1.id_product = p2.id_product
If you want to filter out duplicate id_related meaning multiple occurrence of same id_related is counted as one you can use distinct as
count(distinct id_related)
Ok, here's the deal, I have a table for stats where there are different types and I only want to select a row if it has a previous (not directly previous) row that has some matching data (hash) and is unique based on another column value.
In the following example we need to only get SALE rows
This example should help:
id link_id member_id stat_type hash
----------------------------------------------
108 41 82 SALE fffff
107 41 82 CLICK fffff
106 41 82 CLICK eeeee
105 41 67 SALE ddddd
104 41 67 CLICK ddddd
103 41 35 SALE ccccc
102 41 35 CLICK bbbbb
101 41 35 CLICK aaaaa
The only row I want to get back here is member_id = 67 because that member's only previous CLICK to link_id 41 has the same hash. Members 82 and 35 do not get selected because they both have a previous click to link_id 41 with a mismatched hash.
Query result should be:
id link_id member_id stat_type hash
----------------------------------------------
105 41 67 SALE ddddd
TIA
If I've understood your problem correctly, then you first need to group the table by link_id and member_id, filter those groups for ones that contain only one distinct hash, and then join the results back to your table to obtain all the matching SALE records:
SELECT * FROM my_table NATURAL JOIN (
SELECT link_id, member_id
FROM my_table
GROUP BY link_id, member_id
HAVING COUNT(DISTINCT hash) = 1
) t WHERE stat_type = 'SALE'
See it on sqlfiddle.
One way to get this result is to use an anti-join pattern.
SELECT r.*
FROM mytable r
LEFT
JOIN ( SELECT t.member_id
, t.link_id
, t.hash
FROM mytable t
GROUP
BY t.member_id
, t.link_id
, t.hash
) s
ON s.member_id = r.member_id
AND s.link_id = r.link_id
AND NOT (s.hash = r.hash)
WHERE r.stat_type = 'SALE'
AND s.member_id IS NULL
The inline view (derived table) aliased as s gets all of the distinct hash values for each member_id and link_id. That is left joined to all of the rows in the table that have stat_type='SALE', with matching member_id and link_id, and with a NON-matching hash.
The "trick" is the s.member_id IS NULL predicate. Any rows that found a match (i.e. a non-matching HASH will have a non-null value for s.member_id. Only rows that did not have a match will return a NULL (due to the LEFT JOIN).
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