Mysql - Group by multiple table and columns - mysql

I have the two following tables:
content:
========
cid | iid | qty
---------------
1 | 7 | 42
2 | 7 | 1
3 | 8 | 21
ret:
====
rid | cid | qty
--------------
1 | 1 | 2
2 | 1 | 10
3 | 2 | 1
I would like to retrieve, for each iid, the sum of content.qty and ret.qty
For exemple, for given tables, the result would be:
iid=7, SUM(content.qty) = 43, SUM(ret.qty)=13
iid=8, SUM(content.qty) = 21, SUM(ret.qty)=0
Is there any way to do it in one query?
In advance, thank you!

This is a bit complicated, because you don't want duplicates in your sums. To fix that problem, do the aggregations separately as subqueries. The first is directly on content the second joins back to content from ret to get the iid column.
The following query follows this approach, and assumes that cid is a unique key on content:
select c.iid, c.qty + coalesce(r.qty, 0)
from (select c.iid, SUM(qty) as cqty
from content c
group by c.iid
) c left outer join
(select c.iid, SUM(r.qty) as rqty
from ret r join
content c
on r.cid = c.cid
group by c.iid
) r
on c.iid = r.iid;

Related

How to show data displayed even not matchiing WHERE condition?

I am trying to display some data from database using multiple parameters.
t_item
ID_ITEM ITEM PRICE
A Book 1000
B Pencil 2000
C Pen 3000
t_store_master
ID_STORE STORE
ST01 STORE_1
ST02 STORE_2
ST03 STORE_3
t_store_detail
ID_STORE_DETAIL ID_STORE ID_ITEM LAST_STOCK
1 ST01 A 5
2 ST01 B 7
3 ST02 A 9
4 ST02 C 4
5 ST03 C 8
I'd like to display the data even when I give non existing value as parameter. If I use not existing value as parameter, there is no data retrieved. Some example of the output:
STORE ITEM LAST_STOCK TOTAL
STORE_1 Book 5 5000 //Parameter: (ID_STORE = 'ST01' and ID_ITEM = 'A')
STORE_1 Pen 0 0 //Parameter: (ID_STORE = 'ST01' and ID_ITEM = 'C')
My current query:
SELECT
t_store_master.STORE,
t_item.ITEM,
t_store_detail.LAST_STOCK,
(t_store_detail.LAST_STOCK * t_item.PRICE) AS 'TOTAL'
FROM t_store_master
INNER JOIN t_store_detail ON t_store_master.ID_STORE = t_store_detail.ID_STORE
INNER JOIN t_item ON t_store_detail.ID_ITEM= t_item.ID_ITEM
WHERE t_store_detail.ID_STORE = '?' AND t_store_detail.ID_ITEM = '?'
Note: - Is it also possible to display data with these parameter?
STORE_6 Book 0 0 //Parameter: (ID_STORE = 'ST06' and ID_ITEM = 'A')
STORE_7 - 0 0 //Parameter: (ID_STORE = 'ST07' and ID_ITEM = 'E')
One way is to create a Derived Table, based on your Input Parameter values. If you have multiple combinations of input params, you can utilize UNION to include them all in a single subquery. Now, you can do a LEFT JOIN to all the table(s) to check if any matching row exists or not.
SELECT
prm.ID_STORE,
sm.STORE,
prm.ID_ITEM,
i.ITEM,
sd.LAST_STOCK,
(sd.LAST_STOCK * i.PRICE) AS TOTAL
FROM
-- Change the values in this query depending on parameter accordingly
(SELECT 'ST07' AS ID_STORE, 'E' AS ID_ITEM
-- If you have multiple parameter combinations, you can extend this using UNION
UNION
SELECT 'ST06', 'A') AS prm
LEFT JOIN t_store_master AS sm
ON sm.ID_STORE = prm.ID_STORE
LEFT JOIN t_item AS i
ON i.ID_ITEM = prm.ID_ITEM
LEFT JOIN t_store_detail AS sd
ON sd.ID_STORE = prm.ID_STORE
AND sd.ID_ITEM = prm.ID_ITEM
Result
| ID_STORE | ID_ITEM | STORE | ITEM | LAST_STOCK | TOTAL |
| -------- | ------- | ----- | ---- | ---------- | ----- |
| ST06 | A | | Book | | |
| ST07 | E | | | | |
View on DB Fiddle

Get total count of records with a mysql join and 2 tables

I have 2 tables that I am trying to join but I am not sure how to make it the most time efficient.
Tasks Table:
nid | created_by | claimed_by | urgent
1 | 11 | 22 | 1
2 | 22 | 33 | 1
3 | 33 | 11 | 1
1 | 11 | 43 | 0
1 | 11 | 44 | 1
Employee Table:
userid | name
11 | EmployeeA
22 | EmployeeB
33 | EmployeeC
Result I am trying to get:
userid | created_count | claimed_count | urgent_count
11 | 3 | 1 | 3
22 | 1 | 1 | 2
33 | 1 | 1 | 2
created_account column will show total # of tasks created by that user.
claimed_count column will show total # of tasks claimed by that user.
urgent_count column will show total # of urgent tasks (created or claimed) by that user.
Thanks in advance!
I would start by breaking this up into pieces and then putting them back together. You can get the created_count and claimed_count using simple aggregation like this:
SELECT created_by, COUNT(*) AS created_count
FROM myTable
GROUP BY created_by;
SELECT claimed_by, COUNT(*) AS claimed_count
FROM myTable
GROUP BY claimed_by;
To get the urgent count for each employee, I would join the two tables on the condition that the employee is either the created_by or claimed_by column, and group by employee. Instead of counting, however, I would use SUM(). I am doing this because it appears each row will be either 0 or 1, so SUM() will effectively count all non-zero rows:
SELECT e.userid, SUM(t.urgent)
FROM employee e
JOIN task t ON e.userid IN (t.created_by, t.claimed_by)
GROUP BY e.userid;
Now that you have all the bits of data you need, you can use an outer join to join all of those subqueries to the employees table to get their counts. You can use the COALESCE() function to replace any null counts with 0:
SELECT e.userid, COALESCE(u.urgent_count, 0) AS urgent_count, COALESCE(crt.created_count, 0) AS created_count, COALESCE(clm.claimed_count, 0) AS claimed_count
FROM employee e
LEFT JOIN(
SELECT e.userid, SUM(t.urgent) AS urgent_count
FROM employee e
JOIN task t ON e.userid IN (t.created_by, t.claimed_by)
GROUP BY e.userid) u ON u.userid = e.userid
LEFT JOIN(
SELECT claimed_by, COUNT(*) AS claimed_count
FROM task
GROUP BY claimed_by) clm ON clm.claimed_by = e.userid
LEFT JOIN(
SELECT created_by, COUNT(*) AS created_count
FROM task
GROUP BY created_by) crt ON crt.created_by = e.userid;
Here is an SQL Fiddle example.

MySQL Filtering rows from three tables

Let's say i've got this database:
book
| idBook | name |
|--------|----------|
| 1 |Book#1 |
category
| idCateg| category |
|--------|----------|
| 1 |Adventures|
| 2 |Science F.|
book_categ
| id | idBook | idCateg | DATA |
|--------|--------|----------|--------|
| 1 | 1 | 1 | (null) |
| 2 | 1 | 2 | (null) |
I'm trying to select only the books which are in category 1 AND category 2
This is what I've got so far:
SELECT book.* FROM book,book_categ
WHERE book_categ.idCateg = 1 AND book_categ.idCateg = 2
Obviously, this giving 0 results becouse each row has only one idCateg it does work width OR but the results are not what I need. I've also tried to use a join, but I just can't get the results I expect.
Here it's the SQLFiddle of my current project, the data at the begining is just a sample.
SQLFiddle
Any help will be really appreciated.
You could double join with a constraint on the category id:
SELECT a.* FROM book AS a
INNER JOIN book_categ AS b ON a.idBook = b.idBook AND b.idCateg = 1
INNER JOIN book_categ AS c ON a.idBook = c.idBook AND c.idCateg = 2
You could use a subquery:
SELECT a.* FROM book AS a
WHERE
(SELECT COUNT(DISTINCT idCateg) FROM book_categ AS b
WHERE b.idBook = a.idBook AND b.idCateg IN (1,2)) = 2
If you are on MySQL as your fiddle implies, you should prefer the join variant, since most joins are much faster in MySQL than subqueries.
edit
This one should also work:
SELECT a.* FROM book a
INNER JOIN book_categ AS b ON a.idBook = b.idCateg
WHERE b.idCateg IN (5, 6)
GROUP BY idBook
HAVING COUNT(DISTINCT b.idCateg) = 2
and should be faster than the two above, although you have to change the last number according to the number of category ids you are requesting.

Finding cooccuring values in MYSQL weak relation table

I have a weak relation table, called header, it is basically just three ID's: id is an autoincrement primary key, did points to the id of table D and hid points to the id of table H. D and H are irrelevant here.
I want to find for any value of hid, the other values of hid that shares did with the original hid. An example:
id | did | hid
===============
1 | 1 | 1
2 | 1 | 2
3 | 1 | 3
4 | 2 | 1
5 | 2 | 4
6 | 2 | 5
7 | 3 | 2
8 | 3 | 6
For hid = 1 I would thus like to find id = {2,3,5,6} as those are the rows that have did in common with hid = 1.
I can do this by creating some arrays in PHP and running through all possible values of hid and respective did, but this is a quite slow process for large tables. I was wondering if there is a clever kind of JOIN or similar statement that could be used to find the cooccuring values of hid.
If I have understood you correctly:-
SELECT a.hid, GROUP_CONCAT(b.id)
FROM header a
INNER JOIN header b
ON a.did = b.did
AND b.hid != 1
WHERE a.hid = 1
GROUP BY a.hid
SQL fiddle:-
http://www.sqlfiddle.com/#!2/9aa26/1
Maybe this:
SELECT d.id
FROM (
SELECT *
FROM header
WHERE header.hid =1
) AS h
JOIN header AS d ON d.did = h.did
WHERE d.hid !=1

MySQL selective GROUP BY, using the maximal value

I have the following (simplified) three tables:
user_reservations:
id | user_id |
1 | 3 |
1 | 3 |
user_kar:
id | user_id | szak_id |
1 | 3 | 1 |
2 | 3 | 2 |
szak:
id | name |
1 | A |
2 | B |
Now I would like to count the reservations of the user by the 'szak' name, but I want to have every user counted only for one szak. In this case, user_id has 2 'szak', and if I write a query something like:
SELECT sz.name, COUNT(*) FROM user_reservations r
LEFT JOIN user_kar k ON k.user_id = r.user_id
LEFT JOIN szak s ON r.szak_id = r.id
It will return two rows:
A | 2 |
B | 2 |
However I want to every reservation counted to only one szak (lets say the highest id only). I tried MAX(k.id) with HAVING, but seems uneffective.
I would like to know if there is a supported method for that in MySQL, or should I first pick all the user ID-s on the backend site first, check their maximum kar.user_id, and then count only with those, removing them from the id list, when the given szak is counted, and then build the data back together on the backend side?
Thanks for the help - I was googling around for like 2 hours, but so far, I found no solution, so maybe you could help me.
Something like this?
SELECT sz.name,
Count(*)
FROM (SELECT r.user_id,
Ifnull(Max(k.szak_id), -1) AS max_szak_id
FROM user_reservations r
LEFT OUTER JOIN user_kar k
ON k.user_id = r.user_id
GROUP BY r.user_id) t
LEFT OUTER JOIN szak sz
ON sz.id = t.max_szak_id
GROUP BY sz.name;