How to calculate a level with an unlimited number of levels? - mysql

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.

Related

leetcode 574 winning candidate query

Please see the picture for ERROR SCREENSHOT
Table: Candidate
+-----+---------+
| id | Name |
+-----+---------+
| 1 | A |
| 2 | B |
| 3 | C |
| 4 | D |
| 5 | E |
+-----+---------+
Table: Vote
+-----+--------------+
| id | CandidateId |
+-----+--------------+
| 1 | 2 |
| 2 | 4 |
| 3 | 3 |
| 4 | 2 |
| 5 | 5 |
+-----+--------------+
id is the auto-increment primary key, CandidateId is the id appeared in Candidate table.
Write a sql to find the name of the winning candidate, the above example will return the winner B.
+------+
| Name |
+------+
| B |
+------+
Notes:
You may assume there is no tie, in other words there will be at most one winning candidate.
Why this code can't work? Just try to use without limit
SELECT c.Name AS Name
FROM Candidate AS c
JOIN
(SELECT r.CandidateId AS can, MAX(r.Total_vote) AS big
FROM (SELECT CandidateId, COUNT(id) AS Total_vote
FROM Vote
GROUP BY CandidateId) AS r) AS v
ON c.id = v.can;
In your query, here: SELECT r.CandidateId AS can, MAX(r.Total_vote) AS big
you use MAX aggregate function, without group by, which is not correct SQL.
Try:
SELECT Candidate.* FROM Candidate
JOIN (
SELECT CandidateId, COUNT(id) AS Total_vote
FROM Vote
GROUP BY CandidateId
ORDER BY COUNT(id) DESC LIMIT 1
) v
ON Candidate.id = v.CandidateId
This is a join/group by query with order by:
select c.name
from candidate c join
vote v
on v.candidateid = c.id
group by c.id, c.name
order by count(*) desc
limit 1;
SELECT c.Name AS Name
FROM Candidate AS c JOIN (SELECT r.CandidateId AS can
FROM
(SELECT CandidateId, COUNT(id) AS Total_vote
FROM Vote
GROUP BY CandidateId) AS r
WHERE r.Total_vote = (SELECT MAX(r.Total_vote) FROM (SELECT
CandidateId, COUNT(id) AS Total_vote
FROM Vote
GROUP BY CandidateId) r)) AS v
ON c.id = v.can;
This is updated code
My code has two errors. The first one is "use of an aggregate like Max requires a Group By clause if there are any non-aggregated columns in the select list", but not sure why my previous code still can run and show no error. Maybe the system add the group by function automatically when it run.
The second one is that max can't be used with Group by in this format.

How to properly add an additional column in a SELECT statement in MySQL?

I would like to extract the number of attendances (i.e., COUNT()) of "Coaches" at "Shows" happening during two separate months: March and April. I managed to create a query that collects that number over only one of the months. In addition, via slightly modifying the query, the numbers over the second month can be found easily. But how do I merge them into one table containing both columns?
So, given the two queries and resulting tables below, how would one "append" the result of Query 2 to the result of Query 1? In other words, how would one combine their respective SELECT statements?
I included links to the SQL fiddle in case you need them.
Thank you in advance.
SQL Fiddle
Query 1:
SELECT C.*, COUNT(CIS.idCoach) AS MarchNumOfShows
FROM Coach AS C
LEFT JOIN
(
CoachInShow AS CIS
LEFT JOIN
TVShow AS S
ON S.idShow = CIS.idShow
)
ON C.idCoach = CIS.idCoach AND S.airDate LIKE '_____04___'
GROUP BY C.idCoach
Results:
| idCoach | name | surname | MarchNumOfShows |
|---------|-----------|---------|-----------------|
| 1 | Stephen | Hawking | 5 |
| 2 | Nicholas | Cage | 7 |
| 3 | Sigourney | Weaver | 6 |
Query 2 (Minimal difference, querying for April instead of March):
SELECT COUNT(CIS.idCoach) AS AprilNumOfShows
FROM Coach AS C
LEFT JOIN
(
CoachInShow AS CIS
LEFT JOIN
TVShow AS S
ON S.idShow = CIS.idShow
)
ON C.idCoach = CIS.idCoach AND S.airDate LIKE '_____05___'
GROUP BY C.idCoach
Results:
| AprilNumOfShows |
|-----------------|
| 8 |
| 7 |
| 10 |
Wanted:
| idCoach | name | surname | MarchNumOfShows | AprilNumOfShows |
|---------|-----------|---------|-----------------|-----------------|
| 1 | Stephen | Hawking | 5 | 8 |
| 2 | Nicholas | Cage | 7 | 7 |
| 3 | Sigourney | Weaver | 6 | 10 |
You are very close, the last step you missed is simply combine MarchNumOfShows and AprilNumOfShows with left join.
like below codes (or look into the Sql Fiddle ):
SELECT C.idCoach, C.name, C.surname, COUNT(distinct CIS4.idShow) AS MarchNumOfShows
, COUNT(distinct CIS5.idShow) AS AprilNumOfShows
FROM Coach AS C
LEFT JOIN
(
CoachInShow AS CIS4
LEFT JOIN
TVShow AS S4
ON S4.idShow = CIS4.idShow
)
ON C.idCoach = CIS4.idCoach AND S4.airDate LIKE '_____04___'
LEFT JOIN
(
CoachInShow AS CIS5
LEFT JOIN
TVShow AS S5
ON S5.idShow = CIS5.idShow
)
ON C.idCoach = CIS5.idCoach AND S5.airDate LIKE '_____05___'
GROUP BY C.idCoach;
And below is another way to get the same output (or look into SQL Fiddle):
SELECT C.idCoach, C.name, C.surname,
sum(case when DATE_FORMAT(airDate,'%M')='April' then 1 else null end ) AS AprilNumOfShows,
sum(case when DATE_FORMAT(airDate,'%M')='May' then 1 else null end ) AS MayNumOfShows
FROM Coach AS C
LEFT JOIN
(
CoachInShow AS CIS
LEFT JOIN
TVShow AS S
ON S.idShow = CIS.idShow
)
ON C.idCoach = CIS.idCoach
GROUP BY C.idCoach;
one way to do it is with a case:
select *,
sum(case when airdate like "%03%" then 1 else 0 end) as March,
sum(case when airdate like "%04%" then 1 else 0 end) as April
...

Ordering issue when using SQL variable

I run this query:
SELECT stockcarddetail.id, stockcarddetail.date, stockcarddetail.quantity, stockcarddetail.pricePerItem
FROM Stockcard
LEFT JOIN staff
ON staff.branchId = stockcard.branchId
LEFT JOIN stockcarddetail
ON stockcarddetail.stockcardId = stockcard.id
WHERE staff.username = 'jemmy.h'
AND stockcarddetail.quantity > 0
AND stockcard.productId = '98924a5f-6afb-11e7-8dd4-2c56dcbcb038'
ORDER BY date ASC
and get the result below:
id | date | quantity| pricePerItem
50 | 2017-10-15 | 10.00 | 10000.00
1 | 2017-10-18 | 20.00 | 10000.00
Then, I need to calculate the cumulative of quantity based on the order above, so I run this query:
SELECT a.*, #tot:=#tot + a.quantity FROM
(SELECT #tot:= 0)b
JOIN
(SELECT stockcarddetail.id, stockcarddetail.date, stockcarddetail.quantity, stockcarddetail.pricePerItem
FROM Stockcard
LEFT JOIN staff
ON staff.branchId = stockcard.branchId
LEFT JOIN stockcarddetail
ON stockcarddetail.stockcardId = stockcard.id
WHERE staff.username = 'jemmy.h'
AND stockcarddetail.quantity > 0
AND stockcard.productId = '98924a5f-6afb-11e7-8dd4-2c56dcbcb038'
ORDER BY date ASC) a
Then I got this result:
id | date | quantity| pricePerItem | #tot
1 | 2017-10-18 | 20.00 | 10000.00 | 20
50 | 2017-10-15 | 10.00 | 10000.00 | 30
However, the result that I want is like this:
id | date | quantity| pricePerItem | #tot
50 | 2017-10-15 | 10.00 | 10000.00 | 10
1 | 2017-10-18 | 20.00 | 10000.00 | 30
How can I get the expected result?
EDIT
Simplified version of the problem can be found here: http://sqlfiddle.com/#!9/f6ad91/3
From what I understand from you, you want the cumulative total for each entry.
I suggest ditching the variable and relying on a subquery instead:
SELECT
scd.id,
scd.date,
scd.quantity,
scd.pricePerItem,
(SELECT SUM(scd1.quantity) FROM StockcardDetail AS scd1 WHERE scd1.stockcardId = scd.stockcardId AND scd1.date <= scd.date) AS total
FROM Stockcard
LEFT JOIN staff ON staff.branchId = stockcard.branchId
LEFT JOIN stockcarddetail AS scd ON scd.stockcardId = stockcard.id
WHERE staff.username = 'jemmy.h'
AND scd.quantity > 0
AND stockcard.productId = '98924a5f-6afb-11e7-8dd4-2c56dcbcb038'
ORDER BY scd.date ASC
The idea behind this is to make it select the sum of all entries prior (including the current one) for each entry.
As per my understanding, you should get the expected output from your query. But, you aren't getting your expected output, then other possible solution is (WITHOUT JOIN)
SET #tot:= 0;
SELECT
stockcarddetail.id,
stockcarddetail.date,
stockcarddetail.quantity,
stockcarddetail.pricePerItem,
#tot:=#tot + stockcarddetail.quantity as Total
FROM Stockcard
LEFT JOIN staff ON staff.branchId = stockcard.branchId
LEFT JOIN stockcarddetail ON stockcarddetail.stockcardId = stockcard.id
WHERE staff.username = 'jemmy.h' AND stockcarddetail.quantity > 0 AND stockcard.productId = '98924a5f-6afb-11e7-8dd4-2c56dcbcb038'
ORDER BY date ASC

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.

Filter query with order by and limit when using left join

How to filter query with order by and limit when using left join
store_profile
id + store_name
1 | Accessorize.me
2 | Active IT
3 | Edushop
4 | Gift2Kids
5 | Heavyarm
6 | Bamboo
store_fee
id + store_id + date_end
1 | 1 | 27-6-2013
2 | 2 | 29-8-2013
3 | 3 | 02-6-2013
4 | 4 | 20-4-2013
5 | 4 | 01-7-2013
6 | 4 | 28-9-2013
7 | 5 | 03-9-2013
8 | 6 | 01-9-2013
my previous query
$order_by_for_sort_column = "order by $column" //sorting column
$query = "SELECT * FROM store_profile sp LEFT JOIN store_fee sf ON (sf.store_id = sp.id) $order_by_for_sort_column";
what i want is order by id desc and limit 1 for table store_fee not for for entire query. So i can grab the latest date in date_end for each store.
As you can see for store_id 4(store_fee) i have 3 different date and i just want grab the latest date.
and the result should be something like this
1 | Accessorize.me 27-6-2013
2 | Active IT 29-8-2013
3 | Edushop 02-6-2013
4 | Gift2Kids 28-9-2013
5 | Heavyarm 03-9-2013
6 | Bamboo 01-9-2013
SELECT a.id, a.store_name, MAX(b.date_End) date_end
FROM store_profile a
LEFT JOIN store_fee b
ON a.ID = b.store_ID
GROUP BY a.id, a.store_name
SQLFiddle Demo
but if the datatype date_End column is varchar, the above query won't work because it sorts the value by character and that it can mistakenly gives undesired result. 18-1-2013 is greater than 01-6-2013.
To further gain more knowledge about joins, kindly visit the link below:
Visual Representation of SQL Joins
SELECT *
FROM store_profile AS sp
LEFT JOIN (
SELECT store_id, MAX(date_end)
FROM store_fee
GROUP BY store_id
) AS sf
ON sp.id=sf.store_id;