MySQL SUM and CASE possible on DISTINCTROW? - mysql

I have this query :
SELECT
p.name,
COUNT(DISTINCT t.keyTask) AS totalTasksCount,
SUM(DISTINCT CASE WHEN t.keyPriority = 24 AND (t.keyState = 16 OR t.keyState = 17) THEN 1 ELSE 0 END) AS highPriorityTasksCount,
SUM(DISTINCT CASE WHEN t.keyState = 16 OR t.keyState = 17 THEN 1 ELSE 0 END) AS activesTasksCount,
SUM(DISTINCT t.estimatedDuration) AS estimatedDuration,
SUM(TIMESTAMPDIFF(SECOND,wp.start,wp.end)) * 1000 AS workedDuration
FROM projects_projects p
LEFT JOIN projects_tasks t
ON p.keyProject = t.keyProject
LEFT JOIN projects_workPeriods wp
ON wp.keyTask = t.keyTask
LEFT JOIN common_organizations o
ON o.keyOrganization = p.keyOrganization
LEFT JOIN common_users uc
ON uc.keyUser = p.keyUserCreator
LEFT JOIN common_users uu
ON uu.keyUser = p.keyUserUpdater
GROUP BY
p.keyProject
ORDER BY
highPriorityTasksCount DESC,
activesTasksCount DESC,
p.updated DESC,
p.name;
But the result fields highPriorityTasksCount and activesTasksCount returns 0 or 1 which is normal with this query. I was wondering if there is any way to make DISTINCTROW work on row and not on case result value for these fields without subquery ?
Current result :
p.name,
totalTasksCount,
highPriorityTasksCount,
activesTasksCount,
estimatedDuration,
workedDuration
'Project 1', '4', '1', '1', '14400000', '15300000'
'Project 2', '48', '1', '1', '84600000', '503100000'
'Project 3', '6', '1', '1', '108000000', NULL
'Project 4', '4', '1', '1', '25200000', '30600000'
'Project 5', '5', '1', '1', '226800000', '39600000'
'Project 6', '2', '0', '1', NULL, '10800000'
'Project 7', '9', '0', '1', NULL, '36900000'
Expected result :
'Project 1', '4', '1', '1', '14400000', '15300000'
'Project 2', '48','20', '2', '84600000', '503100000'
'Project 3', '6', '1', '1', '108000000', NULL
'Project 4', '4', '4', '2', '25200000', '30600000'
'Project 5', '5', '5', '1', '226800000', '39600000'
'Project 6', '2', '0', '1', NULL, '10800000'
'Project 7', '9', '0', '1', NULL, '36900000'
EDIT :
Modified query with subqueries since there is no way, help about optimization would be appreciated, this one is working :
SELECT
p.name,
COUNT(DISTINCT t.keyTask) AS totalTasksCount,
/* OLD SUM(DISTINCT CASE WHEN t.keyPriority = 24 AND (t.keyState = 16 OR t.keyState = 17) THEN 1 ELSE 0 END) AS highPriorityTasksCount, */
(
SELECT
SUM(CASE WHEN st.keyPriority = 24 AND (st.keyState = 16 OR st.keyState = 17) THEN 1 ELSE 0 END)
FROM projects_tasks st
WHERE
st.keyProject = p.keyProject
) AS highPriorityTasksCount,
/* OLD SUM(DISTINCT CASE WHEN t.keyState = 16 OR t.keyState = 17 THEN 1 ELSE 0 END) AS activesTasksCount, */
(
SELECT
SUM(CASE WHEN st.keyState = 16 OR st.keyState = 17 THEN 1 ELSE 0 END)
FROM projects_tasks st
WHERE
st.keyProject = p.keyProject
) AS activesTasksCount,
SUM(t.estimatedDuration) AS estimatedDuration,
SUM(TIMESTAMPDIFF(SECOND,wp.start,wp.end)) * 1000 AS workedDuration
FROM projects_projects p
LEFT JOIN projects_tasks t
ON p.keyProject = t.keyProject
LEFT JOIN projects_workPeriods wp
ON wp.keyTask = t.keyTask
LEFT JOIN common_organizations o
ON o.keyOrganization = p.keyOrganization
LEFT JOIN common_users uc
ON uc.keyUser = p.keyUserCreator
LEFT JOIN common_users uu
ON uu.keyUser = p.keyUserUpdater
GROUP BY
p.keyProject
ORDER BY
highPriorityTasksCount DESC,
activesTasksCount DESC,
p.updated DESC,
p.name;

without subquery.
No, you will have to use a subquery, or there's something I don't know about MySQL.

Related

MySQL count(*) returning 0 even though I used IFNULL and COALESCE

Here is my query:
USE adventureWorks4mysql;
SELECT DISTINCT a.city, count(a.city) as "City Count", emp.Gender as Gender, emp.VacationHours as VacationHours,
(select if(count(*) is null,0, count(*))
FROM address aa
inner join employeeaddress empad on aa.AddressID = empad.AddressID
inner join employee emp on empad.EmployeeID = emp.EmployeeID
where MaritalStatus = 'M' and aa.city = a.city
group by aa.City) as married,
(select ifnull(count(*),0)
FROM address aa
inner join employeeaddress empad on aa.AddressID = empad.AddressID
inner join employee emp on empad.EmployeeID = emp.EmployeeID
where MaritalStatus = 'S' and aa.city = a.city
group by aa.City) as single
FROM address a
inner join employeeaddress empad on a.AddressID = empad.AddressID
inner join employee emp on empad.EmployeeID = emp.EmployeeID
group by a.City;
returns the following:
'Bellevue', '36', 'F', '5', '22', '14'
'Berlin', '1', 'F', '35', NULL, '1'
'Bordeaux', '1', 'M', '34', NULL, '1'
'Bothell', '13', 'M', '9', '7', '6'
'Calgary', '1', 'M', '33', '1', NULL
'Cambridge', '2', 'F', '37', '2', NULL
'Carnation', '5', 'M', '77', '4', '1'
'Detroit', '1', 'M', '38', NULL, '1'
'Duluth', '1', 'F', '24', NULL, '1'
'Duvall', '10', 'F', '80', '3', '7'
'Edmonds', '25', 'M', '84', '16', '9'
'Everett', '18', 'M', '42', '11', '7'
'Gold Bar', '5', 'M', '92', '3', '2'
'Index', '5', 'F', '61', '3', '2'
'Issaquah', '15', 'M', '70', '4', '11'
'Kenmore', '12', 'F', '86', '5', '7'
'Kent', '1', 'F', '5', '1', NULL
'Melbourne', '1', 'F', '36', NULL, '1'
'Memphis', '1', 'M', '29', '1', NULL
'Minneapolis', '1', 'M', '48', NULL, '1'
'Monroe', '14', 'M', '21', '4', '10'
'Nevada', '1', 'F', '27', '1', NULL
'Newport Hills', '7', 'M', '44', '2', '5'
'Ottawa', '1', 'M', '31', '1', NULL
'Portland', '1', 'F', '22', NULL, '1'
'Redmond', '21', 'M', '2', '11', '10'
'Renton', '17', 'M', '6', '12', '5'
'Sammamish', '17', 'F', '31', '6', '11'
'San Francisco', '2', 'M', '16', '2', NULL
'Seattle', '44', 'F', '82', '21', '23'
'Snohomish', '10', 'M', '88', '3', '7'
Not at all clear about you desired result, but is you are attempting to count cities, then I suggest you use "conditional aggregates" instead of your current approach, like this:
SELECT
a.city
, COUNT( a.city ) AS "City Count"
, count(CASE WHEN maritalstatus = 'M' THEN a.city END) AS married
, count(CASE WHEN maritalstatus = 'S' THEN a.city END) AS single
FROM address a
INNER JOIN employeeaddress empad ON a.addressid = empad.addressid
INNER JOIN employee emp ON empad.employeeid = emp.employeeid
GROUP BY
a.city;
Note how the case expression is INSIDE the aggregate function COUNT - hence the term "conditional aggregates" e.g. for singles, if there is a singe status then count that address other wise just ignore that row. nb COUNT does not increment if a value is null.
Please also note that you are only grouping by the single column city. If you really want more result rows because of gender and vacationhours then also use those columns in the GROUP BY clause
SELECT
a.city
, emp.gender AS Gender
, emp.vacationhours AS VacationHours
, COUNT( a.city ) AS "City Count"
, count(CASE WHEN maritalstatus = 'M' THEN a.city END) AS married
, count(CASE WHEN maritalstatus = 'S' THEN a.city END) AS single
FROM address a
INNER JOIN employeeaddress empad ON a.addressid = empad.addressid
INNER JOIN employee emp ON empad.employeeid = emp.employeeid
GROUP BY
a.city
, emp.gender
, emp.vacationhours
;

mysqli query for multiple cases with coditions

Need to select data if condition first true then check user id exist, if condition not true then check condition two then check user id not exist
Technologies used: Codigniter - Mysqli
SELECT *
FROM `tablename`
WHERE `user_id` = '1'
OR `user_id` IN('62', '63', '58', '6', '50', '19', '2', '17', '7', '3')
AND `user_id` NOT IN('35')
OR (CASE WHEN except_friends = 1 THEN FIND_IN_SET('1', friends_list), FALSE END CASE WHEN selected_friends = 1 THEN NOT FIND_IN_SET('1', friends_list), FALSE END) ORDER BY `id` desc
Mysql syntax error
I think there is syntax error in your query. try this one
SELECT * FROM tablename
WHERE user_id = '1'
OR user_id IN('62', '63', '58', '6', '50', '19', '2', '17', '7', '3')
AND user_id NOT IN('35')
OR
(CASE
WHEN except_friends = 1
THEN NOT FIND_IN_SET('1', friends_list)
WHEN selected_friends = 1
THEN NOT FIND_IN_SET('1', friends_list)
END) ORDER BY id desc

SQL Get most frequent value from a column based on a condition

This query
SELECT
PlayerID, HeroTypeID, HeroTypeIDCount, Wins / (Losses + Wins) AS WinRate, Wins, Losses
FROM (
SELECT E.PlayerID AS PlayerID,
FK_HeroTypeID AS HeroTypeID,
COUNT(FK_HeroTypeID) AS HeroTypeIDCount,
SUM(CASE WHEN D.Result = 'LOSS' THEN 1 ELSE 0 END) AS Losses,
SUM(CASE WHEN D.Result = 'WIN' THEN 1 ELSE 0 END) AS Wins
FROM GamePlayerDetail D
JOIN Player E
ON D.FK_PlayerID = E.PlayerID
JOIN Game I
ON D.FK_GameID = I.GameID
WHERE PlayedDate BETWEEN DATE_SUB(CURDATE(), INTERVAL 7 DAY) AND CURDATE()
GROUP BY E.PlayerID, FK_HeroTypeID
) AS T
ORDER BY PlayerID
produces the following result:
# PlayerID, HeroTypeID, HeroTypeIDCount, WinRate, Wins, Losses
'1', '11', '1', '1.0000', '1', '0'
'1', '13', '3', '0.3333', '1', '2'
'1', '24', '5', '0.8000', '4', '1'
'1', '27', '1', '1.0000', '1', '0'
'2', '28', '1', '0.0000', '0', '1'
'2', '6', '1', '0.0000', '0', '1'
'2', '30', '1', '0.0000', '0', '1'
'2', '7', '1', '1.0000', '1', '0'
What I'd like to do is get the most frequent FK_HeroTypeID (which is also highest value of HeroTypeIDCount) per PlayerID, but in case of ties, the highest winrate should take precedence. Here's an example of what I'd like to get:
PlayerID, HeroTypeID, HeroTypeIDCount, WinRate, Wins, Losses
1, 24, 5, 0.8000, 4, 1
2, 7, 1, 1.0000, 1, 0
How should you write a query like this?
Edit:
Ok, here's a simple Create/Insert table for the produced result.
http://sqlfiddle.com/#!9/d644a
SELECT playerid
, herotypeid
, herotypeidcount
, winrate
, wins
, losses
FROM
( SELECT *
, CASE WHEN #prev=playerid THEN #i:=#i+1 ELSE #i:=1 END rank
, #prev:=playerid prev
FROM table1
, (SELECT #prev:=null,#i:=0) vars
ORDER
BY herotypeidcount DESC
, winrate DESC
) x
WHERE rank = 1;
Here's a 'hack' solution. It works, but really shouldn't be relied upon...
SELECT *
FROM
( SELECT *
FROM table1
ORDER
BY herotypeidcount DESC
, winrate DESC
) x
GROUP
BY playerid

SQL nested select sum returns wrong integer

I have the following select statement:
SELECT
T.name,
C.name as competence_name,
THC.competence_level_id as requiredLevel,
(SELECT SUM(UHC.competence_level_id) FROM user_has_competence UHC INNER JOIN user U ON U.id = UHC.user_id WHERE competence_id = C.id AND U.title_id = T.id GROUP BY T.id) as current_level,
(SELECT
ROUND(SUM(UHC.competence_level_id)/ COUNT(UHC.user_id))
FROM
user_has_competence UHC
JOIN user U ON U.id = UHC.user_id
WHERE
competence_id = C.id
AND U.title_id = T.id GROUP BY T.id) - THC.competence_level_id as gap,
C.id
FROM
title_has_competence THC
JOIN
title T ON T.id = THC.title_id
JOIN
competence C ON C.id = THC.competence_id
This returns the following result:
'Head of IT', 'Office', '7', '16', '1', '524'
'Head of IT', 'Empatisk', '2', '5', '1', '527'
'Head of IT', 'Målrettet', '5', '12', '1', '529'
'Head of IT', 'Udadvendt', '10', NULL, NULL, '525'
'Webudvikler', 'Office', '2', '3', '1', '524'
'Webudvikler', 'Outlook', '3', '4', '1', '526'
'Webudvikler', 'Målrettet', '6', '10', '4', '529'
'Webudvikler', 'Back-end', '9', '9', '0', '534'
'Webudvikler', 'Infosoft', '10', '5', '-5', '532'
However the result is invalid:
In the first row you will see the current level sum is = 16 this should be 9 (6 + 3)
The gap is also incorrect as the gap should be 9 / 2 = 4.5 - 7 = -2.5
So my question is what am i doing wrong? why is my SUM() function returning way to much.
Also note that the COUNT() returns the right value = 2
The Tables
title_has_competence
id title_id competence_id competence_level_id
'82', '165', '527', '2'
'85', '166', '524', '2'
'86', '166', '526', '3'
'83', '165', '529', '5'
'87', '166', '529', '6'
'81', '165', '524', '7'
'88', '166', '534', '9'
'84', '165', '525', '10'
'89', '166', '532', '10'
User_has_competence
user_id, competence_id, competence_level_id, progression
'1', '524', '6', '0'
'1', '527', '4', '0'
'1', '529', '3', '0'
'2', '524', '10', '0'
'2', '527', '1', '0'
'2', '529', '9', '0'
'3', '524', '3', '0'
'3', '526', '4', '0'
'3', '529', '10', '0'
'3', '532', '5', '0'
'3', '534', '9', '0'
title
id, name organization_id
'165', 'Head of IT', '1'
'166', 'Webudvikler', '1'
User
id, username, password, title_id,
1 ** , ** , 165
2 ** , ** , 165
3 ** , ** , 166

Conditional MySQL Column

Table: qa_selltypes
selltype_code, selltype_name
'1', 'unit'
'2', 'pound'
'3', 'box'
'4', 'gallon'
Table: qa_items
item_code, item_name, selltype_code, item_price
'1', 'PS2 Keyboard', '1', '10.45'
'2', 'Screws', '3', '15.02'
'3', 'Oil', '2', '30.00'
I need to refill a grid that contains four fields (unit, stall, pound, gallon) establishing item_price according to the relation to the field of selltype_code, one example: The rest set them to 0
DGV:
item_code, item_name, unit, box, pound, gallon
'1', 'PS2 Keyboard', '10.45', '0', '0', '0'
'2', 'Screws', '0', '0', '15.02', '0'
'3', 'Oil', '0', '30.00', '0', '0'
select i.item_code, i.item_name,
case when q.selltype_name = 'unit' then i.item_price else 0 end as unit,
case when q.selltype_name = 'box' then i.item_price else 0 end as box,
case when q.selltype_name = 'pound' then i.item_price else 0 end as pound,
case when q.selltype_name = 'gallon' then i.item_price else 0 end as gallon
from qa_items i
inner join qa_selltypes q on i.selltype_code = q.selltype_code
You can use a case. Example:
select case when some_column = 'abc'
then 'hello'
else 'good bye'
end as conditional_column
from your_table