**Im trying to get the difference between two tables sums but i keep getting the wrong outcome **
Table1 Table2
| product | quantity | | product | quantity |
| -------- | -------- | | -------- | -------- |
| a | 7 | | a | 2 |
| b | 8 | | b | 4 |
| c | 9 | | c | 1 |
| c | 7 | | c | 3 |
| a | 3 | | a | 2 |
| b | 4 | | b | 3 |
I tried this queury but i got the wrong values:
select table1.product, sum(table1.quantity) - sum(table2.quantity) as difference
from table1
join table2 on table1.product = table2.product
group by table1.product,table2.product;
Expected Outcome
Table1
product
difference
a
6
b
5
c
12
UNION ALL the tables (where you take -quantity for table2.) GROUP BY the result.
select product, sum(quantity)
from
(
select product, quantity from table1
union all
select product, -quantity from table2
) dt
group by product
As requested - some comments:
As a general advice, it's safer to GROUP BY (in subqueries) before joining - since a JOIN can result in multiple rows for a value.
Also, to include a product only found in one of the tables, an outer join would have been needed.
Related
I have some table like this
table request_buys
| id | invoice | user_id |
| -- | ----------------- | ------- |
| 3 | 20220405/01104298 | 1 |
table traces
| id | request_buy_id | status_id | created_at |
| -- | -------------- | --------- | ------------------- |
| 37 | 3 | 1 | 2022-03-27 14:12:25 |
| 38 | 3 | 2 | 2022-03-28 14:12:25 |
| 39 | 3 | 3 | 2022-03-29 14:12:25 |
| 40 | 3 | 4 | 2022-03-30 14:12:25 |
| 41 | 3 | 5 | 2022-03-31 14:12:25 |
| 42 | 3 | 6 | 2022-04-01 14:12:25 |
table statuses
| id | nama |
| -- | ----------------- |
| 1 | Order Placed |
| 2 | Order Paid |
| 3 | Accepted |
| 4 | Picked by Courier |
| 5 | In Transit |
| 6 | Delivered |
| 7 | Rated |
| 8 | Rejected |
| 9 | Canceled |
and then i try to design query like below
select
request_buys.invoice,
MAX(traces.id) as traces_id,
MAX(statuses.nama) as statuses_nama
from
`request_buys`
inner join `traces` on `request_buys`.`id` = `traces`.`request_buy_id`
inner join `statuses` on `traces`.`status_id` = `statuses`.`id`
where
`user_id` = 1
group by
request_buys.id
and produces output like the following
output
| invoice | traces_id | statuses_nama |
| ----------------- | --------- | ----------------- |
| 20220405/01104298 | 42 | Picked by Courier |
and the output i expect should be like in the table below
expect
| invoice | traces_id | statuses_nama |
| ----------------- | --------- | ----------------- |
| 20220405/01104298 | 42 | Delivered |
I understand my error is in MAX(statuses.nama) which I should change like removing MAX() in statuses.nama
But i just get error like this "SELECT list is not in GROUP BY clause and contains nonaggregated ... this is incompatible with sql_mode=only_full_group_by"
then I tried some to clear the value "ONLY_FULL_GROUP_BY" with a query like the following
SET sql_mode=(SELECT REPLACE(##sql_mode,'ONLY_FULL_GROUP_BY',''))
and the result is like this
output
| invoice | traces_id | statuses_nama |
| ----------------- | --------- | ----------------- |
| 20220405/01104298 | 42 | Order Placed |
and I'm really stuck at this
and how to make trace_id.status_id from the "GROUP BY" result based on request_buys.id still have a relationship with statuses.id
Your problem lies with your misuse of the MAX(statuses.nama) expression. Based on your expected output,you intend to get the statuses.nama which matches the MAX(traces.id), NOT the MAX(statuses.nama) value which returns the highest value in terms of alphabetic order. In this case, the initial letter 'P' > 'D' . I have tweaked your code a bit and tried it on workbench,supposing there are more than one invoice for a particular user.(e.g insert into request_buys values (4,'20230405/01104298',1); insert into traces values (43,4,7,'2022-04-01 14:12:25');) It works as intended.
select invoice, t.id as traces_id, s.nama as statuses_name from request_buys r
join traces t on r.id=t.request_buy_id
join statuses s on t.status_id=s.id
join
(select traces.request_buy_id, MAX(traces.id) as traces_id
from `request_buys`
inner join `traces` on `request_buys`.`id` = `traces`.`request_buy_id`
where
`user_id` = 1
group by
traces.request_buy_id ) join_t
on t.request_buy_id=join_t.request_buy_id and t.id=join_t.traces_id
;
If I'm understanding correctly, you're trying to retrieve the most recent status for each invoice. Using MAX(nama) won't return that result, because it just picks the maximum status name alphabetically.
Assuming you're using MySQL 8.x, use ROW_NUMBER() to sort and rank the statuses for each invoice, by the most recent date first. Then grab the latest one using where rowNum = 1
WITH cte AS (
SELECT rb.id AS request_buy_id
, rb.invoice
, t.id AS traces_id
, s.nama AS statuses_nama
, ROW_NUMBER() OVER(PARTITION BY rb.id ORDER BY t.created_at DESC) AS RowNum
FROM request_buys rb
INNER JOIN traces t ON rb.id = t.request_buy_id
INNER JOIN statuses s ON t.status_id = s.id
WHERE user_id = 1
)
SELECT *
FROM cte
WHERE RowNum = 1
;
Result:
request_buy_id
invoice
traces_id
statuses_nama
RowNum
3
20220405/01104298
42
Delivered
1
db<>fiddle here
I'm currently learning the ropes of SQL and i have an tutorial from school that goes like this:
All stores (storeid) sells (productid, storeid) some products (productid)
A store is considered a monopoly if every product they sell is not sold by any other store.
How do I find the monopolies?
I was thinking of selecting the storeid from 2 of the same tables, but I'm not sure how to continue from there on.
Tables are below:
Store:
+-----------+
| storeid |
+-----------+
| --------- |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
+-----------+
Products:
+-------------+
| productid |
+-------------+
| --------- |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
+-------------+
Sells:
+--------------------------+
| productid | storeid |
+--------------------------+
| -----------+------------ |
| 1 | 1 |
| 2 | 1 |
| 2 | 2 |
| 3 | 2 |
| 1 | 2 |
| 3 | 3 |
| 2 | 4 |
| 4 | 4 |
| 5 | 5 |
| 6 | 5 |
+--------------------------+
So by my count, only store 5 is considered a monopoly, because they sell products that are not available in other stores.
We can try a self join approach combined with aggregation:
SELECT t1.storeid
FROM yourTable t1
LEFT JOIN yourTable t2
ON t2.productid = t1.productid AND
t2.store_id <> t1.storeid
GROUP BY t1.storeid
HAVING COUNT(t2.storeid) = 0;
The approach here is to try to match each row in Sells to some other row on the condition that it is the same product, but is being sold by some other store. A matching store is one for which none of its products are being sold by other stores, so the count of the second table column in the join should be zero.
Use window functions and aggregation:
select s.storeid
from (select s.*,
count(*) over (partition by productid) as num_stores
from sells s
) s
group by s.storeid
having max(num_stores) = 1;
This should be much faster than a self-join. It is also almost a direct translation of your question. The subquery counts the number of stores where each product is sold. The outer query selects stores where all products are sold in one store.
So I have this table
table: "tbl_hash"
------------------------------
| id | hash1 | hash2 | hash3 |
------------------------------
| 1 | a | b | c |
| 2 | a | b | c |
| 3 | a | g | d |
| 4 | a | g | d |
| 5 | a | g | d |
------------------------------
I only want to group them by hash1, hash2, and hash3. count them and only return the count which is higher by 2.
So I have this query to get the values I wanted:
select CONCAT(hash1, hash2, hash3) as hashes, COUNT(*) as count from `tbl_hash` group by hashes having `count` > 2 limit 5
^^ the query above works perfectly..
But what If I wanted to get the data and count for each row? Expected output:
--------------------------------------
| id | hash1 | hash2 | hash3 | count |
--------------------------------------
| 1 | a | b | c | 2 |
| 2 | a | b | c | 2 |
| 3 | a | g | d | 3 |
| 4 | a | g | d | 3 |
| 5 | a | g | d | 3 |
--------------------------------------
I'm also planning on converting those to a query builder using DB::table...
You may join your original table to a subquery which finds the counts for each group:
SELECT t1.*, t2.cnt
FROM tbl_hash t1
INNER JOIN
(
SELECT hash1, hash2, hash3, COUNT(*) AS cnt
FROM tbl_hash
GROUP BY hash1, hash2, hash3
HAVING COUNT(*) > 2
) t2
ON t1.hash1 = t2.hash1 AND
t1.hash2 = t2.hash2 AND
t1.hash3 = t2.hash3;
Note that what I wrote above would completely filter off any original records belonging to a hash1/hash2/hash3 group which did not have a count greater than 2. If you instead want all records, with the count, then remove the HAVING clause.
As a side note, in databases which support analytic functions, such as SQL Server and Oracle, we could write a much less verbose query using COUNT as an analytic function. At some point, mainstream versions of MySQL will also support this. But for now, we are stuck with doing a join.
Table 1 shows subjects and its components.
Table 2 shows marks for each subject and its corresponding component.
Table 2 is related to Table 1 by its primary key.
The second image shows the expected output where each row shows the count of the components for the particular subject for that particular student and also the sum of marks that student secured in that subject( sum of marks of all components in that subject).
I've tried with many numbers of group by statements but still couldn't reach the required output.
It would be of great help if a valid solution is provided.
Thanks in advance.
Try the following queries:
Query-1:
SELECT sub_id, COUNT(component_id) t1_component_count
FROM table1
GROUP BY sub_id;
Output:
+--------+--------------------+
| sub_id | t1_component_count |
+--------+--------------------+
| s1 | 3 |
| s2 | 3 |
| s3 | 1 |
| s4 | 1 |
+--------+--------------------+
Query-2:
SELECT t2.stu_id, t1.sub_id, t3.comp_cnt AS t1_component_count,
count(t1.component_id) AS t2_component_count, sum(t2.stu_marks) total_marks
FROM table2 t2 INNER JOIN table1 t1 ON t2.t1_id = t1.t1_id
INNER JOIN
(SELECT sub_id, COUNT(component_id) comp_cnt FROM table1 GROUP BY sub_id)
AS t3 ON t1.sub_id = t3.sub_id
GROUP BY t2.stu_id, t1.sub_id;
Output:
+--------+--------+--------------------+--------------------+-------------+
| stu_id | sub_id | t1_component_count | t2_component_count | total_marks |
+--------+--------+--------------------+--------------------+-------------+
| stu1 | s1 | 3 | 3 | 100 |
| stu1 | s2 | 3 | 2 | 130 |
| stu2 | s3 | 1 | 1 | 50 |
| stu3 | s2 | 3 | 1 | 30 |
| stu4 | s1 | 3 | 1 | 90 |
+--------+--------+--------------------+--------------------+-------------+
I have 3 tables like this
SecretAgents
| id | name |
|----|------|
| 1 | A |
| 2 | B |
Victims
| id | name | agent_id |
|----|------|----------|
| 1 | Z | 1 |
| 2 | Y | 1 |
| 3 | X | 2 |
Data
| id | keys | values | victim_id | form_id |
|----|------|--------|-----------|---------|
| 1 | a1 | x | 1 | 1 |
| 2 | a2 | xx | 1 | 2 |
| 3 | a3 | xxx | 2 | 1 |
| 4 | a5 | xxx | 1 | 1 |
I have to get the count of forms(here victim_id and form_id are composite primary keys) and the count of victims for each agent.
I have tried this for any 2 tables with left joins and group by but I am not able to achieve the same together. If anyone can be generous enough to offer a pointer/solution, that would be super awesome..
EDIT 1: The query
This is definitely not the right query but anyways
SELECT count(DISTINCT v.id) as victimcount, `sa`.`username`, `sa`.`id`, count(DISTINCT d.form_id) as submissions
FROM `SecretAgents` as `sa`
LEFT JOIN `Victims` as `v` ON `v`.`agent_id`=`sa`.`id`
LEFT JOIN `Data` as `d` ON `d`.`victim_id`=`v`.`id`
GROUP BY `v`.`agent_id`
ORDER BY `sa`.`id` ASC
The victimcount is correct but the submissions count becomes wrong. Tried lots of other things too but this is the most relevant...
Thanks
I believe you can count the forms-per-agent like so:
SELECT COUNT(*) as form_count, a.id as id, a.name as agent
FROM Data d
LEFT JOIN Victims v ON v.id = d.victim_id
LEFT JOIN SecretAgents a on v.agent_id = a.id
GROUP BY a.id;
To count the victims, just leave off the Data table.