How to return all rows in a column when using cross apply and DelimitedSplit8K_LEAD together in SQL Server? - case
This code works to return the info that I need from the column but IT IS ONLY RETURNING ONE ROW and I need it to return them all.
You can leverage the DelimitedSplit8K_LEAD function which you can read more about here. https://www.sqlservercentral.com/articles/reaping-the-benefits-of-the-window-functions-in-t-sql-2 You will also find the code for the function there.
select FirstValue = max(case when s.ItemNumber = 12 then s.Item end)
, SecondValue = max(case when s.ItemNumber = 45 then try_convert(datetime, stuff(stuff(stuff(s.Item, 9, 0, ' '), 12, 0, ':'), 15, 0, '.')) end)
from myDatabase x
cross apply DelimitedSplit8K_LEAD(x.myColumn, ',') s
where s.ItemNumber = 12
or s.ItemNumber = 45
Here is an example of the data in the column that I'm trying to return.
,505611,XXXXXXX,,,,,,,,,13M2,,,,,,,,,,,03294961,,,,,,,,,,,,,,,,,,,,,XXXXX,20220216183348,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,US,,,0000,,,,,,,,,,,,,,,,,,,,
Here is an example of it working, just not with using it on the table column https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=ca74e807853eb1dea445a8ffb7209b70
Okay so here is an example. Create a table in sql server database...
CREATE TABLE honda
(
user1 nvarchar(max)
);
INSERT INTO honda
(user1)
VALUES
(',523869,HXMFG-01,,,,,,,,,11M2,,,,,,,,,,,03311141,,,,,,,,,,,,,,,,,,,,,EAGLE,20220323082041,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,US,,,0000,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,'),
(',523869,HXMFG-01,,,,,,,,,12M2,,,,,,,,,,,03311148,,,,,,,,,,,,,,,,,,,,,EAGLE,20220323093049,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,US,,,0000,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,'),
(',523869,HXMFG-01,,,,,,,,,13M2,,,,,,,,,,,03311216,,,,,,,,,,,,,,,,,,,,,EAGLE,20220323100350,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,US,,,0000,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,'),
(',523869,HXMFG-01,,,,,,,,,14M2,,,,,,,,,,,03311242,,,,,,,,,,,,,,,,,,,,,EAGLE,20220323103854,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,US,,,0000,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,'),
(',523869,HXMFG-01,,,,,,,,,15M2,,,,,,,,,,,03311267,,,,,,,,,,,,,,,,,,,,,EAGLE,20220323112420,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,US,,,0000,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,'),
(',527040,HXMFG-01,,,,,,,,,16M2,,,,,,,,,,,03311352,,,,,,,,,,,,,,,,,,,,,EAGLE,20220323122930,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,US,,,0000,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,'),
(',527040,HXMFG-01,,,,,,,,,17M2,,,,,,,,,,,03311395,,,,,,,,,,,,,,,,,,,,,EAGLE,20220323130347,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,US,,,0000,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,');
If using sql server should only have to run this middle block of code once.
CREATE FUNCTION [dbo].[DelimitedSplit8K_LEAD]
--===== Define I/O parameters
(#pString VARCHAR(8000), #pDelimiter CHAR(1))
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
--===== "Inline" CTE Driven "Tally Tableā produces values from 0 up to 10,000...
-- enough to cover VARCHAR(8000)
WITH E1(N) AS (
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
), --10E+1 or 10 rows
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS (--==== This provides the "zero base" and limits the number of rows right up front
-- for both a performance gain and prevention of accidental "overruns"
SELECT 0 UNION ALL
SELECT TOP (DATALENGTH(ISNULL(#pString,1))) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
SELECT t.N+1
FROM cteTally t
WHERE (SUBSTRING(#pString,t.N,1) = #pDelimiter OR t.N = 0)
)
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY s.N1),
Item = SUBSTRING(#pString,s.N1,ISNULL(NULLIF((LEAD(s.N1,1,1) OVER (ORDER BY s.N1) - 1),0)-s.N1,8000))
FROM cteStart s
;
GO
If using dbfiddle take the GO off of the end in the above block.
select ECI_Level = max(case when s.ItemNumber = 12 then s.Item end)
, 'DateTime' = max(case when s.ItemNumber = 45 then try_convert(datetime, stuff(stuff(stuff(s.Item, 9, 0, ' '), 12, 0, ':'), 15, 0, '.')) end)
from honda x
cross apply DelimitedSplit8K_LEAD(x.user1, ',') s
where s.ItemNumber = 12
or s.ItemNumber = 45
I was able to get it to work by adding GROUP BY like this...
select ECI_Level = max(case when s.ItemNumber = 12 then s.Item end)
, 'DateTime' = max(case when s.ItemNumber = 45 then try_convert(datetime, stuff(stuff(stuff(s.Item, 9, 0, ' '), 12, 0, ':'), 15, 0, '.')) end)
from honda x
cross apply DelimitedSplit8K_LEAD(x.user1, ',') s
where s.ItemNumber = 12
or s.ItemNumber = 45
GROUP BY user1;
The problem is that you are aggregating over the whole honda table, rather than just over the split data.
You need to place the aggregation into an APPLY
select s.*
from honda x
CROSS apply (
SELECT ECI_Level = max(case when s.ItemNumber = 12 then s.Item end)
, DateTime = max(case when s.ItemNumber = 45 then try_convert(datetime, stuff(stuff(stuff(s.Item, 9, 0, ' '), 12, 0, ':'), 15, 0, '.')) end)
FROM DelimitedSplit8K_LEAD(x.user1, ',') s
where s.ItemNumber = 12
or s.ItemNumber = 45
) s;
db<>fiddle
Personally, for just two values I wouldn't bother with a split function. Instead you can use CHARINDEX, feeding each one into the next to get the next , location.
And I guess this just shows why you shouldn't store data in a database like this in the first place.
Related
Recursive join and group_concat with SQLite
I have the following table: id value successor_id -- ----- ------------ 1 v1 2 2 v2 4 4 v3 null 7 v4 9 9 v5 null 12 v6 null Note: Those are simple paths (no trees), so two ids can not have the same successor_id. Also, the ids are ordered and a successor must be the following id. E.g. in my example the only possible successor of id 2 is 4. It cannot be 1 or 7. Now, I want to do some kind of recursive LEFT JOIN ON id = successor_id with the table itself and GROUP_CONCAT the values in order to get the following result: min_id max_id values ------ ------ -------- 1 4 v1,v2,v3 7 9 v4,v5 12 12 v6 How can I achieve this? I guess a combination of WITH RECURSIVE and GROUP BY, but I don't know how to start since WITH RECURSIVE requires a starting point, but I have multiple starting points (ids: 1, 7 and 12). Here is the SQL code to create the example table: .mode column .nullvalue null .width -1 -1 -1 CREATE TABLE test ( id INTEGER NOT NULL, value STRING NOT NULL, successor_id INTEGER ); INSERT INTO test (id, value, successor_id) VALUES ( 1, 'v1', 2); INSERT INTO test (id, value, successor_id) VALUES ( 2, 'v2', 4); INSERT INTO test (id, value, successor_id) VALUES ( 4, 'v3', null); INSERT INTO test (id, value, successor_id) VALUES ( 7, 'v4', 9); INSERT INTO test (id, value, successor_id) VALUES ( 9, 'v5', null); INSERT INTO test (id, value, successor_id) VALUES (12, 'v6', null); SELECT id, value, successor_id FROM test;
There is no need for a recursive query. Use window functions: WITH cte AS ( SELECT *, LAG(successor_id) OVER (ORDER BY id) IS NULL flag FROM test ) SELECT DISTINCT MIN(id) OVER (PARTITION BY grp) min_id, MAX(id) OVER (PARTITION BY grp) max_id, GROUP_CONCAT(value) OVER ( PARTITION BY grp ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) "values" FROM ( SELECT *, SUM(flag) OVER (ORDER BY id) grp FROM cte ); See the demo.
SQLite - Create dummy variable vector/string from multiple columns
I have some data that looks like this: UserID Category ------ -------- 1 a 1 b 2 c 3 b 3 a 3 c A I'd like to binary-encode this grouped by UserID: three different values exist in Category, so a binary encoding would be something like: UserID encoding ------ -------- 1 "1, 1, 0" 2 "0, 0, 1" 3 "1, 1, 1" i.e., all three values are present for UserID = 3, so the corresponding vector is "1, 1, 1". Is there a way to do this without doing a bunch of CASE WHEN statements? There may be dozens of possible values in Category
Cross join the distinct users to distinct categories and left join to the table. Then use GROUP_CONCAT() window function which supports an ORDER BY clause, to collect the 0s and 1s: WITH users AS (SELECT DISTINCT UserID FROM tablename), categories AS ( SELECT DISTINCT Category, DENSE_RANK() OVER (ORDER BY Category) rn FROM tablename ), cte AS ( SELECT u.UserID, c.rn, '"' || GROUP_CONCAT(t.UserID IS NOT NULL) OVER (PARTITION BY u.UserID ORDER BY c.rn) || '"' encoding FROM users u CROSS JOIN categories c LEFT JOIN tablename t ON t.UserID = u.UserID AND t.Category = c.Category ) SELECT DISTINCT userID, FIRST_VALUE(encoding) OVER (PARTITION BY UserID ORDER BY rn DESC) encoding FROM cte ORDER BY userID This will work for any number of categories. See the demo. Results: UserID encoding 1 "1,1,0" 2 "0,0,1" 3 "1,1,1"
First create an encoding table to explicit establish order of categories in the bitmap: create table e (Category int, Encoding int); insert into e values ('a', 1), ('b', 2), ('c', 4); First generate a list of users u (cross) joined with the encoding table e to get a fully populated (UserId, Category, Encoding) table. Then left join the fully populated table with the user supplied data t. The right hand side t can now be used to drive if we need to set a bit or not: select u.UserId, '"' || group_concat(case when t.UserId is null then 0 else 1 end, ', ') || '"' 'encoding' from (select distinct UserID from t) u join e left natural join t group by 1 order by e.Encoding and it gives the expected result: 1|"1, 1, 0" 2|"0, 0, 1" 3|"1, 1, 1"
SQLite convert single row to multiple rows
SQLite I want to convert single row value seperate by ',' to multiple rows Example : Single_Row 6,7,8,9,10,11,12,13,14,15,16 Result must be : MultipleRows 6 7 8 9 10 12 13 14 15 16 I tried doing it with substr function but getting unexpected result select numbers.n, substr(CbahiHSSpecialtyUnits.units,numbers.n,1) from numbers inner join CbahiHSSpecialtyUnits on LENGTH(CbahiHSSpecialtyUnits.units) - LENGTH(REPLACE(CbahiHSSpecialtyUnits.units, ',', ''))>=numbers.n-1 WHERE HsSubStandardID=22 and SpecialtyID=2 and numbers.n>0 order by numbers.n; One good thing is I'm getting number of rows correct.. But the values that should be separated is wrong .. Please note numbers table is I have created for indexing purpose, with the help of this post. SQL split values to multiple rows
You can do it with a recursive CTE: WITH cte AS ( SELECT SUBSTR(Units, 1, INSTR(Units || ',', ',') - 1) col, SUBSTR(Units, INSTR(Units || ',', ',') + 1) value FROM CbahiHSSpecialtyUnits WHERE HsSubStandardID=22 AND SpecialtyID = 2 UNION ALL SELECT SUBSTR(value, 1, INSTR(value || ',', ',') - 1), SUBSTR(value, INSTR(value || ',', ',') + 1) FROM cte WHERE LENGTH(value) > 0 ) SELECT col FROM cte WHERE col + 0 > 0 Or, if you know the upper limit of the numbers is, say 20 and there are no duplicates among the numbers: WITH cte AS (SELECT 1 col UNION ALL SELECT col + 1 FROM cte WHERE col < 20) SELECT c.col FROM cte c INNER JOIN CbahiHSSpecialtyUnits u ON ',' || u.Units || ',' LIKE '%,' || c.col || ',%' WHERE HsSubStandardID=22 AND SpecialtyID = 2 See the demo. Results: col 6 7 8 9 10 11 12 13 14 15 16
I got the solution http://www.samuelbosch.com/2018/02/split-into-rows-sqlite.html WITH RECURSIVE split(predictorset_id, predictor_name, rest) AS ( SELECT CbahiHSSpecialtyUnits.SpclUnitSerial, '', units || ',' FROM CbahiHSSpecialtyUnits WHERE HsSubStandardID=22 and SpecialtyID=2 UNION ALL SELECT predictorset_id, substr(rest, 0, instr(rest, ',')), substr(rest, instr(rest, ',')+1) FROM split WHERE rest <> ''
How to calculate a row value based on the previous row value in the same column
I have the following data set: DATE CODE RANK PARTITION ? ABS 0 1 12/04/2014 RET 1 1 20/04/2014 RET 2 1 01/05/2014 ABS 2 1 13/05/2014 RET 2 1 01/06/2015 ABS 2 1 09/10/2015 RETk 2 1 ? ABS 0 2 02/04/2015 RET 1 2 03/04/2015 RET 2 2 04/04/2015 ABS 2 2 05/04/2015 STT 3 2 06/04/2015 RETk 4 2 07/04/2015 RETk 4 2 RANK is the column I want to calculate in my SQL given the columns DATE, CODE AND the previous value of the same column. It's initialized here to 0. The logic I want to implement is as follows: If RANK-1 (previous row) IS NULL AND CODE = ABS THEN RANK = 0 If RANK-1 (previous row) IS NULL AND CODE <> ABS THEN RANK <- (RANK-1) + 1 If RANK-1 = 0 or 1 AND CODE = RET THEN RANK <- (RANK-1) + 1 If RANK-1 = 2 AND CODE = STT THEN RANK <- (RANK-1) + 1 If RANK-1 = 3 AND CODE = RETk THEN RANK <- (RANK-1) + 1 If CODE = ABS THEN RANK <- (RANK-1) (previous row) Else 0 The Teradata release I am using is R14. The calculation is done on a partition basis as shown in the example above. I have added some more constraints in the model to make it clearer. In this example, if the current code is RET, I do not increase the rank until the previous one is 0 or 1. Similarly, If my current code is RETk, I do not increase the rank until the previous one is equal to 3, otherwise, I do not change the rank. I repeat the same process in the following partition and so on ... I cannot figure out how to update the current column value given the previous one... I tried many logic implementation with OLAP functions without success. Can anyone give me a hint? Thank you very much for your help
You can always use a recursive query for tasks like this. But performance will be bad unless the number of rows per group is low. First you need a way to advance to the next row, as the next row's date can't be calculated based on the current row's date you must materialize the data and add a ROW_NUMBER: CREATE TABLE tab(dt DATE, CODE VARCHAR(10), rnk INT, part INT); INSERT INTO tab( NULL,'ABS' ,0 , 1); INSERT INTO tab(DATE'2014-04-12','RET' ,1 , 1); INSERT INTO tab(DATE'2014-04-20','RET' ,2 , 1); INSERT INTO tab(DATE'2014-05-01','ABS' ,2 , 1); INSERT INTO tab(DATE'2014-05-13','RET' ,2 , 1); INSERT INTO tab(DATE'2014-06-01','ABS' ,2 , 1); INSERT INTO tab(DATE'2014-10-09','RETk',2 , 1); INSERT INTO tab( NULL,'ABS' ,0 , 2); INSERT INTO tab(DATE'2015-04-02','RET' ,1 , 2); INSERT INTO tab(DATE'2015-04-03','RET' ,2 , 2); INSERT INTO tab(DATE'2015-04-04','ABS' ,2 , 2); INSERT INTO tab(DATE'2015-04-05','STT' ,3 , 2); INSERT INTO tab(DATE'2015-04-06','RETk',4 , 2); INSERT INTO tab(DATE'2015-04-07','RETk',4 , 2); CREATE VOLATILE TABLE vt AS ( SELECT dt, code, part -- used to find the next row ,ROW_NUMBER() OVER (PARTITION BY part ORDER BY dt) AS rn FROM tab ) WITH DATA PRIMARY INDEX(part, rn) ON COMMIT PRESERVE ROWS ; And now it's just applying your logic using CASE row after row: WITH RECURSIVE cte (dt, code, rnk, part, rn) AS ( SELECT dt ,code ,CASE WHEN code = 'ABS' THEN 0 ELSE 1 END ,part ,rn FROM vt WHERE rn = 1 UNION ALL SELECT vt.dt ,vt.code ,CASE WHEN cte.rnk IN (0,1) AND vt.CODE = 'RET' THEN cte.rnk + 1 WHEN cte.rnk = 2 AND vt.CODE = 'STT' THEN cte.rnk + 1 WHEN cte.rnk = 3 AND vt.CODE = 'RETk' THEN cte.rnk + 1 WHEN vt.CODE = 'ABS' THEN cte.rnk ELSE cte.rnk END ,vt.part ,vt.rn FROM vt JOIN cte ON vt.part =cte.part AND vt.rn =cte.rn + 1 ) SELECT * FROM cte ORDER BY part, dt; But I think your logic is not actually like this (based on the previous rows exact RANK value), you're just stuck in procedural thinking :-) You might be able to do what you want using OLAP-functions only...
Something along the lines of: create table table1 ( datecol date, code varchar(10), rankcol integer ); --insert into table1 select '2014/05/13', 'RETj', 0; select case when s1.code='ABS' and s2.rankcol = 1 then 1 when s1.code='RET' and s2.rankcol = 0 then 1 when s1.code='RET' and s2.rankcol = 1 then 2 else 0 end RET_res, s1.*, s2.* from (select rankcol, code, row_number() OVER (order by datecol) var1 from table1) s1, (select rankcol, code, row_number() OVER (order by datecol) var1 from table1) s2 where s1.var1=s2.var1-1 order by s1.var1 ;
Group by not returning 0 value
My table contains pk_id,reviewer_id,rating. There are 4 type of rating. 1-very good. 2-good. 3-bad. 4-very bad. I want to calculate how much rating given by each reviewer. Means: If Akee having id 200 has given 2 very good,4 good,3 bad and zero very bad rating to different code. I want result count--- rate 2---------1 4---------2 3---------3 0---------4 My query is SELECT COUNT(RATE),RATE FROM CODE_REVIEW WHERE CODE_REVIEWER_ID= 200 GROUP BY RATE; It is showing result count--- rate 2---------1 4---------2 3---------3 I want to show the fourth row that is 4 rating zero. How can it be done??
If Rate is not the primary key in another table then you need define your own list of rates so MySQL knows what the permutations of rate are: SELECT Rates.Rate, COUNT(Code_Review.Rate) AS CountOfRate FROM ( SELECT 1 AS Rate UNION ALL SELECT 2 AS Rate UNION ALL SELECT 3 AS Rate UNION ALL SELECT 4 ) AS Rates LEFT JOIN Code_Review ON Code_Review.Rate = Rates.Rate AND CODE_REVIEWER_ID = 200 GROUP BY Rates.Rate
Try this query: SELECT coalesce(c.cnt, 0), r.rate FROM (SELECT 1 AS rate UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4) AS r LEFT JOIN (SELECT COUNT(RATE),RATE FROM CODE_REVIEW WHERE CODE_REVIEWER_ID= 200 GROUP BY RATE) AS c ON r.rate = c.rate; The first subquery creates a list of possible rates. You can avoid it if you have a table which defines all rates; Second subquery is yours; LEFT JOIN guarantees that all rates will be shown; coalesce() is needed to convert NULL into 0.
Assuming that you do not have a separate table where the rates are defined. SElECT * from ( SELECT distinct(m.rate), countrate from code_review m LEFT JOIN (SELECT COUNT(rate) as countrate,rate FROM code_review WHERE code_reviewer_id=200 GROUP BY rate) t ON m.rate=t.rate) a
You could do it somthing like this SELECT rates.RATE , SUM(COUNT) COUNT FROM ( SELECT 1 RATE, 0 COUNT UNION ALL SELECT 2 RATE, 0 COUNT UNION ALL SELECT 3 RATE, 0 COUNT UNION ALL SELECT 4 RATE, 0 COUNT ) Rates LEFT JOIN ( SELECT RATE , COUNT(RATE) COUNT FROM CODE_REVIEW WHERE CODE_REVIEWER_ID= 200 GROUP BY RATE ) Ratings200 ON Ratings200.RATE = Rates.RATE
If you can, you should push to try to get it in column format as it is simple as: SELECT SUM(rate = 1) AS 1, SUM(rate = 2) AS 2, SUM(rate = 3) AS 3, SUM(rate = 4) AS 4 FROM code_review WHERE code_reviewer_id = 200 But if you really need a row format, you could do: SELECT a.rate, COUNT(b.rate) AS cnt FROM ( SELECT 1 AS rate UNION ALL SELECT 2 AS rate UNION ALL SELECT 3 AS rate UNION ALL SELECT 4 AS rate ) a LEFT JOIN code_review b ON a.rate = b.rate AND code_reviewer_id = 200 GROUP BY a.rate
SELECT Rate, totCount FROM ( Select Rate, count(Rate) as totCount from Code_Review where CODE_REVIEWER_ID = 200 group by Rate union select 4, 0 union select 3, 0 union select 2, 0 union select 1, 0 ) AS T group by T.Rate