MySQL Order By With Grades As String But Want Integer - mysql

I want to order grades that are a string in the following order.
K, 1, 2, 4, .... 12
But I keep getting 5, 4, 3, ... 12, K, 1
The grade column in sims_classroom is VARCHAR(255) and the table is latin1;
I have tried every cast and trick in the book. At the moment I have the following trick by adding 0 to it. What am I doing wrong?
SELECT
(SELECT district_get_name_function(sod.parent_id, sod.id)) AS 'district_name',
(SELECT school_get_name_function(so.id)) AS 'school_name',
st.teacher_username,
st.teacher_first_name,
st.teacher_last_name,
c.name as 'classroom_name',
c.grade,
c.id,
d.name AS 'discipline_name',
d.id AS 'discipline_id',
lc.name AS 'program_name',
lc.id AS 'program_id'
FROM sims_classroom c
.
.
.
ORDER BY
CASE lower(sort_direction) WHEN 'asc' THEN
CASE lower(sort_order)
WHEN 'grade' THEN
CASE c.grade
WHEN 'K' THEN 0
ELSE (c.grade + 0)
END
WHEN 'teachername' THEN lower(st.teacher_first_name)
ELSE c.name
END
END ASC,
CASE lower(sort_direction) WHEN 'desc' THEN
CASE lower(sort_order)
WHEN 'grade' THEN
CASE c.grade
WHEN 'K' THEN 10000
ELSE (c.grade + 0)
END
WHEN 'teachername' THEN lower(st.teacher_first_name)
ELSE c.name
END
END DESC

You could create a lookup table for the grades. It would have the Grade_Name (i.e. 'K','1','2', etc) and a Grade_Order as an int.
You'd have records like this:
Grade_Name Grade_Order
K 1
1 2
2 3
3 4
and so on.
Then you can add that lookup table to your JOIN and ORDER BY Grade_Order.

I could only get it to work doing this.
WHEN 'grade' THEN
CASE c.grade
WHEN 'K' THEN '00'
WHEN '9' THEN '09'
WHEN '8' THEN '08'
WHEN '7' THEN '07'
WHEN '6' THEN '06'
WHEN '5' THEN '05'
WHEN '4' THEN '04'
WHEN '3' THEN '03'
WHEN '2' THEN '02'
WHEN '1-2' THEN '01'
WHEN '1' THEN '01'
ELSE c.grade
END

Related

Case statement not working with or condition

Please help me with this code.
SELECT (CASE LEFT(BRANCH_CODE, 1)
WHEN '1' THEN 'NL'
WHEN '2' THEN 'MM'
WHEN '3' THEN 'SL'
WHEN '4' THEN 'VIS'
WHEN '5' THEN 'MIN'
WHEN ('7' OR '8') THEN 'SA'
END) `REGION`,
This is the result:
Region
null
MM
The result should be like this:
Region
SA
MM
The following syntax is not valid:
WHEN ('7' OR '8') THEN 'SA'
What follows WHEN or ELSE using this style of CASE expression can only be a literal value, not a logical expression. So, you could refactor to:
SELECT
CASE LEFT(BRANCH_CODE, 1)
WHEN '1' THEN 'NL'
WHEN '2' THEN 'MM'
WHEN '3' THEN 'SL'
WHEN '4' THEN 'VIS'
WHEN '5' THEN 'MIN'
WHEN '7' THEN 'SA' -- repeat the 'SA' predicate twice
WHEN '8' THEN 'SA' END AS REGION
FROM yourTable;
If you wanted to combine the 7 and 8 cases in a single WHEN, then you would have to use the longer form of CASE:
SELECT
CASE WHEN LEFT(BRANCH_CODE, 1) = '1' THEN 'NL'
WHEN LEFT(BRANCH_CODE, 1) = '2' THEN 'MM'
WHEN LEFT(BRANCH_CODE, 1) = '3' THEN 'SL'
WHEN LEFT(BRANCH_CODE, 1) = '4' THEN 'VIS'
WHEN LEFT(BRANCH_CODE, 1) = '5' THEN 'MIN'
WHEN LEFT(BRANCH_CODE, 1) = '7' OR
LEFT(BRANCH_CODE, 1) = '8' THEN 'SA' END AS REGION
FROM yourTable;
This second version is fairly ugly IMHO, and I would probably stick with the first version, which just repeats the SA predicate in two places.
I had similar issues when trying to apply multiple condition statements in "CASE" statement.
I solved it by moving the test after the "WHEN" statement as following :
SELECT (CASE
WHEN LEFT(BRANCH_CODE, 1) = '1' THEN 'NL'
WHEN LEFT(BRANCH_CODE, 1) = '2' THEN 'MM'
WHEN LEFT(BRANCH_CODE, 1) = '3' THEN 'SL'
WHEN LEFT(BRANCH_CODE, 1) = '4' THEN 'VIS'
WHEN LEFT(BRANCH_CODE, 1) = '5' THEN 'MIN'
WHEN LEFT(BRANCH_CODE, 1) IN ('7' OR '8') THEN 'SA'
END) `REGION`,

Return 0 for SQL count when CASE does not exist

I have two tables Log and Player. Where Log stores each game play log with a playerId and date. and the Player table has the Players info as Age and Gender ..etc. I'm writing an SQL stored procedure that takes two dates and will count the LogId and group by age range and gender between the two dates. but when i run the SQL procedure, it doesn't show all the Age_Range/Gender entries when no player exists in that period.I'm trying to get all Age_Range/Gender entries with the their actual count or count= 0 if they don't exist.
I've even tried changing
count(L.LogId) as Count,
To
count(IFNULL(L.LogId, 0)) as Count,
My SQL procedure is :
CREATE PROCEDURE `SHOW_AGE_RANGE` (IN date1 CHAR(10), IN date2 CHAR(10))
BEGIN
SELECT
count(L.LogId) as Count,
CASE
WHEN P.age BETWEEN 13 AND 18 THEN '13-18'
WHEN P.age BETWEEN 19 AND 25 THEN '19-25'
WHEN P.age BETWEEN 26 AND 39 THEN '26-39'
WHEN P.age BETWEEN 40 AND 59 THEN '40-59'
WHEN P.age > 59 THEN '60+'
END as Age_Range,
CASE
WHEN P.gender = 0 then 'Female'
WHEN P.gender = 1 then 'Male'
END as Gender
FROM Log L
LEFT JOIN Player P ON L.playerId = P.playerId
WHERE CAST(L.createdDate AS DATE) BETWEEN CAST(date1 AS DATE) AND CAST(date2 AS DATE)
GROUP BY Age_Range, Gender;
END
Output of the SQL Procedure:
Count Age_Range Gender
----------------------------------
'1' '13-18' 'Male'
'1' '19-25' 'Female'
'3' '26-39' 'Female'
'2' '40-59' 'Male'
'1' '60+' 'Female'
The Expected Output
Count Age_Range Gender
----------------------------------
'0' '13-18' 'Female'
'1' '13-18' 'Male'
'1' '19-25' 'Female'
'0' '19-25' 'Male'
'3' '26-39' 'Female'
'0' '26-39' 'Male'
'0' '40-59' 'Female'
'2' '40-59' 'Male'
'1' '60+' 'Female'
'0' '60+' 'Male'
You need to start out with your age ranges and genders. Really, you're querying off of those and the player data is just something that you're adding to them. Since you don't already have the age ranges in a table (you should probably consider adding that) then you'll need to create a virtual table for those as a subquery.
SELECT
COUNT(L.playerId) AS cnt,
AG.age_range,
G.gender
FROM
(
SELECT 13 AS min_age, 18 AS max_age, '13-18' AS age_range
UNION ALL
SELECT 19 AS min_age, 25 AS max_age, '19-' AS age_range
UNION ALL
SELECT 26 AS min_age, 39 AS max_age, '26-39' AS age_range
UNION ALL
SELECT 40 AS min_age, 59 AS max_age, '40-59' AS age_range
UNION ALL
SELECT 60 AS min_age, 999 AS max_age, '60+' AS age_range
) AS AG
CROSS JOIN
(
SELECT 0 AS gender_value, 'Female' AS gender
UNION ALL
SELECT 1 AS gender_value, 'Male' AS gender
) AS G
LEFT JOIN Player P ON
P.age BETWEEN AG.min_age AND AG.max_age AND
P.gender = G.gender_value
LEFT OUTER JOIN Log L ON
L.playerId = P.playerId AND
CAST(L.createdDate AS DATE) BETWEEN CAST(date1 AS DATE) AND CAST(date2 AS DATE)
GROUP BY
AG.age_range, G.gender
It looks like you want the outer join to the Log table. Swap the Player and Log tables, and relocate the predicate on the createdDate column to the ON clause.
Like this:
FROM Player P
LEFT
JOIN Log L
ON L.playerId = P.playerId
AND CAST(L.createdDate AS DATE) BETWEEN CAST(date1 AS DATE) AND CAST(date2 AS DATE)
GROUP BY Age_Range, Gender;

my mysql SELECT case can't return 3 columns

I'm trying to return 3 columns with the query below. the current query works fine.
SELECT cat, COUNT(*) as count FROM
(SELECT case WHEN `cat_type` = 'PREMIUM' then '1'
WHEN `cat_type` = 'NOT PREMIUM' then '2'
WHEN `cat_type` = 'GOLD' then '3'
WHEN `cat_type` = 'EXECUTIVE' then '4'
WHEN `cat_type` = 'NOT PROVIDED' then '-1'
else '-2'
end AS cat FROM `ab` AS s
JOIN `make` AS m
WHERE s.make_code = m.make_code
) AS someRandomAliasHere
GROUP BY cat
ORDER BY CAST(cat AS UNSIGNED) ASC
when i try and return another column cat_type i get an error
SELECT cat, cat_type, COUNT(*) as count FROM ....
The error i get is
#1054 - Unknown column 'cat_type' in 'field list'
but cat_type does exist in my ab table. any idea what i'm missing? Thanks
Your cat_type exists in ab table.
But your ab table does not exists in your main select.
It only exists within the sub query which returns someRandomAliasHere to you.
So you should likely select cat_type from the same select as you select your cases.
Then you should have access to it, but it would then likely mess with your group by / count.
You'll properly need a more advanced query for what you're after.
You need to include cat_type in the subquery. I would recommend:
SELECT cat, cat_type, COUNT(*) as count
FROM (SELECT (case WHEN `cat_type` = 'PREMIUM' then 1
WHEN `cat_type` = 'NOT PREMIUM' then 2
WHEN `cat_type` = 'GOLD' then 3
WHEN `cat_type` = 'EXECUTIVE' then 4
WHEN `cat_type` = 'NOT PROVIDED' then -1
else -2
end) AS cat, *
FROM `ab` AS s JOIN
`make` AS m
USING (make_code)
) AS someRandomAliasHere
GROUP BY cat, cat_type
ORDER BY abs(cat) asc
Note the following:
Added * to the subquery to capture all the columns.
Changed the join syntax. Not only are explicit joins better but the using clause allows the use of * without having to worry about duplicate names among the joined column.
Removed the single quotes from the constant values for cat. You are treating these as numbers in the order by, so use numbers for the values.
Changed the order by to abs() rather than casting to unsigned. However, do you really want unsigned here?
You're not selecting cat_type into someRandomAliasHere, hence it's not available to your main select. This should fix it:
SELECT cat, cat_type, COUNT(*) as count FROM
(SELECT case WHEN `cat_type` = 'PREMIUM' then '1'
WHEN `cat_type` = 'NOT PREMIUM' then '2'
WHEN `cat_type` = 'GOLD' then '3'
WHEN `cat_type` = 'EXECUTIVE' then '4'
WHEN `cat_type` = 'NOT PROVIDED' then '-1'
else '-2'
end AS cat, cat_type FROM `ab` AS s
JOIN `make` AS m
WHERE s.make_code = m.make_code
) AS someRandomAliasHere
GROUP BY cat, cat_type
ORDER BY CAST(cat AS UNSIGNED) ASC
You needed to include the column in subquery first and then reference that using the table.col
Try below
SELECT someRandomAliasHere.cat_type, cat, COUNT(*) as count FROM
(SELECT cat_type, case WHEN `cat_type` = 'PREMIUM' then '1'
WHEN `cat_type` = 'NOT PREMIUM' then '2'
WHEN `cat_type` = 'GOLD' then '3'
WHEN `cat_type` = 'EXECUTIVE' then '4'
WHEN `cat_type` = 'NOT PROVIDED' then '-1'
else '-2'
end AS cat FROM `ab` AS s
JOIN `make` AS m
WHERE s.make_code = m.make_code
) AS someRandomAliasHere
GROUP BY cat
ORDER BY CAST(cat AS UNSIGNED) ASC
You need to also select the column cat_type, so that someRandomAliasHere owns the column.
SELECT cat, cat_type, COUNT(*) as count FROM
(SELECT case WHEN `cat_type` = 'PREMIUM' then '1'
WHEN `cat_type` = 'NOT PREMIUM' then '2'
WHEN `cat_type` = 'GOLD' then '3'
WHEN `cat_type` = 'EXECUTIVE' then '4'
WHEN `cat_type` = 'NOT PROVIDED' then '-1'
else '-2'
end AS cat, cat_type FROM `ab` AS s
JOIN `make` AS m
WHERE s.make_code = m.make_code
) AS someRandomAliasHere
GROUP BY cat, cat_type
ORDER BY CAST(cat AS UNSIGNED) ASC
Dont forget to add cat_type in GROUP BY too.

Combine results from 2 SQL Server queries into 2 columns

I currently have 2 SQL queries:
select
SUM(CASE T1.DOCTYPE
WHEN '1' THEN T1.CURTRXAM *1
WHEN '4' THEN T1.CURTRXAM *-1
WHEN '5' THEN T1.CURTRXAM *-1
WHEN '6' THEN T1.CURTRXAM *-1
END) as [Payables TB]
from PM20000 T1
select
sum(PERDBLNC) as [GL Balance]
from GL10110
where ACTINDX = '130'
which return 2 results like this:
Payables TB
1520512.30
GL Balance
-1520512.30
I would like to combine the results into 2 columns and have a variance column like below -
Payables TB GL Balance Variance
1520512.30 -1520512.30 0.00
Thank you
simply
select
(select
SUM(CASE T1.DOCTYPE
WHEN '1' THEN T1.CURTRXAM *1
WHEN '4' THEN T1.CURTRXAM *-1
WHEN '5' THEN T1.CURTRXAM *-1
WHEN '6' THEN T1.CURTRXAM *-1
END) as [Payables TB]
from PM20000 T1) as Payables TB,
(select
sum(PERDBLNC) as [GL Balance]
from GL10110
where ACTINDX = '130') as GL Balance,
0.00 as Variance
You can wrap these into CTE's to reuse the values to compute the difference. With no join condition you will just need to CROSS JOIN, as long as these return just one row each :
WITH Payables AS
(
SELECT
SUM(
CASE
WHEN T1.DOCTYPE IN ('1') THEN T1.CURTRXAM *1
WHEN T1.DOCTYPE IN ('4','5','6') THEN T1.CURTRXAM *-1
-- ? ELSE
END) as [Payables TB]
FR PM20000 T1
),
Balance AS
(
SELECT
SUM(PERDBLNC) as [GL Balance]
FROM GL10110
WHERE ACTINDX = '130'
)
SELECT
Payables.[Payables TB],
Balance.[GL Balance],
Payables.[Payables TB] + Balance.[GL Balance] AS Variance
FROM
Payables, Balance; -- OR Payables CROSS JOIN Balance
Since you seem to be doing the same projection for T1.DOCTYPE 4, 5 and 6 in the first query, you can replace it with a CASE WHEN x IN (...)

MYSQL different conditions in a single query

Hello
I have following columns in mysql table: rating1, rating2, price, cond, approved
Is it possible to select results like this:
select
average rating1 + rating2 as total_rating,
average rating1 as rating1,
average rating2 as rating2,
average price if cond = '1' as price_used
average price if cond = '2' as price_new
where approved = '1'
So far I have:
SELECT
(AVG(t.rating1) + AVG(t.rating2)) / 2 AS total_rating
AVG(t.rating1) AS rating1,
AVG(t.rating2) AS rating2,
---- price statements?? ----
FROM t
WHERE 1=1
AND t.approved = '1'
Many thanks and excuse me for my English
Try this:
SELECT (AVG(t.rating1) + AVG(t.rating2)) / 2 AS total_rating,
AVG(t.rating1) AS rating1,
AVG(t.rating2) AS rating2,
AVG(IF(cond='1', price, NULL)) price_used,
AVG(IF(cond='2', price, NULL)) price_new
FROM t
WHERE 1=1
AND t.approved = '1'
EDIT: Updated the query to get desired result.
Standard SQL, works across "all" major dbms:
select (avg(t.rating1) + avg(t.rating2)) / 2 as total_rating
,avg(t.rating1) as rating1
,avg(t.rating2) as rating2
,avg(case when cond = '1' then price end) as price_used
,avg(case when cond = '2' then price end) as price_new
from t
where t.approved = '1'
I don't think doing an average on the If statement will work. But here is the syntax for the IF.
IF(condition, value_to_display_if_true, value_to_display_if_false)
So, for example, IF(1=1, 'true', 'false') would always display 'true' for this column because 1 does = 1.