I have this table register:
id quantity type
1 | 10 | in
2 | 5 | in
1 | 3 | out
1 | 2 | out
2 | 5 | out
3 | 2 | in
3 | 1 | out
I want the balance of each stock *sum of type='in' - sum of type= 'out'*.
Desired output would be:
1 | 5
2 | 0
3 | 1
I also have another table item:
id | name
1 | A
2 | B
3 | C
Is it possible to view the output with the item name instead of the id?
So the final result is like:
A | 5
B | 0
C | 1
The basic idea is conditional aggregation --case inside of sum(). You also need a join to get the name:
select i.name,
sum(case when r.type = 'in' then quantity
when r.type = 'out' then - quantity
else 0
end) as balance
from register r join
item i
on r.id = i.id
group by i.name;
Acccording to description as mentioned in above question,as a solution to it please try executing following SQL query
SELECT i.name,
#in_total:= (select sum(quantity) from register where type = 'in'
and id = r.id group by id),
#out_total:= (select sum(quantity) from register where type = 'out'
and id = r.id group by id),
#balance:= (#in_total - #out_total) as balance
FROM `register`
as r join item i on r.id = i.id group by r.id
CROSS JOIN (SELECT #in_total := 0,
#out_total := 0,
#balance := 0) AS user_init_vars
Related
Now I have that structure (very simplify):
promotion:
id | name | level1 | points1| level2 | points2 | client_id
1 | A | 10 | 12 | 20 | 15 | 1
client:
id | name | value
1 | john | 15
And that's how I calculate the level:
SELECT
name,
CASE
WHEN client.value >= promotion.level2 THEN promotion.points2
WHEN client.value >= promotion.level1 THEN promotion.points1
ELSE "None"
END as points
FROM promotion
JOIN client ON client.id = promotion.client_id
This is working well, but I would like to have such structure:
promotion:
id | name | client_id
1 | A | 1
level:
id | name | level | points | promotion_id
1 | level1 | 10 | 12 | 1
2 | level2 | 10 | 15 | 1
client:
id | name | value
1 | john | 15
But I don't have any idea how to use it in my query to get points...
SELECT
name,
CASE
???
END as points
FROM promotion
JOIN client ON client.id = promotion.client_id
LEFT JOIN level ON promotion.id= level.promotion_id
This query will give you the desired results. It uses a LEFT JOIN to find any rows in the level table which are lower than the client value, and then takes the MAX of those values, using COALESCE to set the value to None if there are no levels below the client's value:
SELECT p.name AS promotion,
c.name AS client,
COALESCE(MAX(l.points), 'None') AS points
FROM promotion p
JOIN client c ON c.id = p.client_id
LEFT JOIN level l ON l.promotion_id = p.id AND l.level < c.value
GROUP BY p.name, c.name
Output for your sample data:
promotion client points
A john 12
Demo on dbfiddle
I would use a correlated subquery:
select p.name,
(select l.points
from level l
where l.promotion_id = p.points and
l.level <= c.value
order by l.level desc
limit 1
) as points
from promotion p join
client c
on c.id = p.client_id;
The point of using a correlated subquery is to avoid an aggregation on the full data. With an index on level(promotion_id, level, points), this should have better performance.
Table Structure
Table 1
-------------
code | name
-------------
1 | abc
2 | xyz
Table 2
-------------------------------
code | table1_code | data
-------------------------------
1 | 1 | a
2 | 1 | b
3 | 1 | c
4 | 1 | d
5 | 1 | e
6 | 2 | f
7 | 2 | g
Now Expected Result:
Result
table1_code | name | data_col1 | data_col2 | data_col3
------------------------------------------------------------------
1 abc a b c
2 xyz f g -
What i have tried so far
SELECT a.code AS table1_code,
a.NAME,
b.data_col
FROM table1 AS a
LEFT OUTER JOIN table2 AS b
ON a.code = b.table1_code
kindly help me to alter the query to get above output,
i want data upto 3 columns only.
To do this you need a way to determine what the first three items in each group should be. The way to do this is to add a row number that restarts for each new group. This would be easy in any database that supports window function, but MySQL doesn't so it gets a bit more complicated as you have to use user defined variables to do the ranking.
In the query below ranking of data is done in the derived table used as source and then a conditional aggregation is performed so that you get one row per group:
SELECT
code,
name,
MAX(CASE WHEN rank = 1 THEN data END) data_col1,
MAX(CASE WHEN rank = 2 THEN data END) data_col2,
MAX(CASE WHEN rank = 3 THEN data END) data_col3
FROM (
SELECT
a.code,
a.name,
b.data,
(
CASE a.name
WHEN #grp THEN #row := #row + 1
ELSE #row := 1 AND #grp := a.name
END
) + 1 AS rank
FROM table1 AS a
LEFT OUTER JOIN table2 AS b ON a.code = b.table1_code
, (SELECT #row := 0, #grp := '') r
ORDER BY a.code, a.name asc
) src
GROUP BY code, name;
Sample SQL Fiddle
I need to create procedure which will find the worst user in one table by counting status with 'P' and 'U' calculate ratio then compare it with other users, take that user id and find it in another table and write all user information that are in two tables. And i call that procedure from java application.
Table Rezervacija
id | SifKorisnikPK | Status
1 | 1 | 'P'
2 | 1 | 'U'
3 | 1 | 'U'
4 | 2 | 'U'
5 | 2 | 'P'
6 | 2 | 'P'
7 | 2 | 'P'
8 | 2 | 'P'
9 | 3 | 'U'
10 | 3 | 'U'
11 | 3 | 'P'
12 | 3 | 'P'
13 | 3 | 'P'
14 | 3 | 'P'
So the user with id 2 is worst user because of 4 P's, and one U, so his ratio is 3 P. Then it's should go to Korisnik table and return all the info for user with id 2
I try with this but can't get any return values
CREATE PROCEDURE sp_getBadPremiumUsers
AS
BEGIN
DECLARE #BrLr int
DECLARE #BrDr int
SELECT #BrLr = (SELECT COUNT(*) FROM Rezervacija A
INNER JOIN Rezervacija B
ON A.SifKorisnikPK = B.SifKorisnikPK
WHERE A.Status = 'P')
SELECT #BrDr = (SELECT COUNT(*) FROM Rezervacija A
INNER JOIN Rezervacija B
ON A.SifKorisnikPK = B.SifKorisnikPK
WHERE A.Status = 'U')
SELECT * INTO #PremiKoris FROM Korisnik
INNER JOIN PremiumKorisnik
ON SifKorisnik = SifKorisnikPK
ALTER TABLE #PremiKoris
DROP COLUMN Password
SELECT * FROM #PremiKoris
WHERE #BrLr > #BrDr
DROP TABLE #PremiKoris
END
GO
You can get the worse user using:
select r.SifKorisnikPK
from Rezervacija r
where status = 'P'
group by SifKorisnikPK
order by count(*) desc
limit 1;
You can then use this in a query to get more information:
select k.*
from PremiumKorisnik k join
(select r.SifKorisnikPK
from Rezervacija r
where status = 'P'
group by SifKorisnikPK
order by count(*) desc
limit 1
) r
on r.SifKorisnikPK = k.SifKorisnikPK
This does in one query what you describe you want to do.
This question is based on: Select row from left join table where multiple conditions are true
I am now trying to select rows from Table 1, which do not have a connection in Table 2 to a certain property ID.
These are the tables:
Table 1
| ID | Name |
| 1 | test |
| 2 | hello |
Table 2
| ID | PropertyID |
| 1 | 3 |
| 1 | 6 |
| 1 | 7 |
| 2 | 6 |
| 2 | 1 |
I am using the following query (which is working with '='):
SELECT tab1ID
FROM table2
WHERE propertyID != 3 OR propertyID = 6
GROUP BY tab1ID
HAVING COUNT(*) = 2;
This query should return ID=2, but it returns zero rows. What I am doing wrong?
Any help is greatly appreciated!
Edit: I had given a MWE but this is my actual query:
SELECT transactionline.total FROM transactionline
LEFT JOIN product_variant ON product_variant.SKU = transactionline.SKU
LEFT JOIN product ON product_variant.productID = product.productID
LEFT JOIN connect_option_product ON connect_option_product.productID = product.productID
LEFT JOIN productattribute_option ON productattribute_option.optionID = connect_option_product.optionID
WHERE productattribute_option.optionID = 4 OR productattribute_option.optionID = 9
GROUP BY transactionline.lineID
HAVING COUNT(*) = 1
AND SUM(productattribute_option.optionID = 4) = 0
AND SUM(productattribute_option.optionID = 9) > 0
A product can have multiple connections to the optionID's. The goal of this query is to select the total amount where some filters are true or false.
Your grouping is correct. But you need to count how many times the value you do not want is in your group. That count must be zero.
SELECT tab1ID
FROM table2
GROUP BY tab1ID
HAVING sum(propertyID = 6) > 0
AND sum(propertyID = 3) = 0
I currently have the following tables:
Case_Workflows
case_id (PK) | work_id (PK) | date
1 | 1 | 2011-12-12
1 | 4 | 2011-12-13
2 | 6 | 2011-12-18
Workflows
work_id (PK) | status_id
1 | 1
2 | 1
3 | 1
4 | 2
5 | 2
6 | 3
Statuses
status_id (PK) | title
1 | abc
2 | def
3 | ghi
What I am attempting to do is pull a count of the total number of cases with a specific status such as 'abc'. The snag is that each case can have multiple workflows and I only want the single most recent one for each case.
The end result should be:
Status: abc - Count: 2
This is what I have so far:
SELECT COUNT(cases.case_id) as countNum
FROM $this->_caseTable
JOIN case_workflows
ON cases.case_id = cases_workflows.case_id
JOIN workflows
ON cases_workflows.workflow_id = workflows.workflow_id
JOIN statuses
ON workflow.status_id = statuses.status_id
WHERE cases.date > '2011-12-12'
AND cases.date <= '2011-12-18'
What I am unsure on is how to first select the latest work_id for each case, and then grabbing its status_id to match it to a WHERE clause such as WHERE statuses.title = 'abc'
SELECT COUNT(*) as countNum
FROM $this->_caseTable
JOIN workflows
ON workflows.workflow_id =
( SELECT workflow_id
FROM cases_workflows AS mcwf
WHERE mcwf.case_id = cases.case_id
ORDER BY date DESC
LIMIT 1
)
JOIN statuses
ON workflow.status_id = statuses.status_id
WHERE cases.date > '2011-12-12'
AND cases.date <= '2011-12-18'
AND statuses.title = 'abc'
From what I'm understanding here, you need to add statuses.title to your SELECT clause, and then add a GROUP BY clause:
SELECT statuses.title, COUNT(cases.case_id) as countNum
FROM $this->_caseTable
JOIN (SELECT case_id, work_id, max(date)
FROM case_workflows
GROUP BY work_id
WHERE case_id = cases.case_id) cw
ON cases.case_id = cw.case_id
JOIN workflows
ON cw.workflow_id = workflows.workflow_id
JOIN statuses
ON workflow.status_id = statuses.status_id
GROUP BY statuses.title
WHERE cases.date > '2011-12-12'
AND cases.date <= '2011-12-18'