Convert SQL rows to columns - mysql

This is my table:
id | num | comment
---+-----+--------
3 | 10 | hello
3 | 20 | pls
3 | 30 | respond
7 | 10 | leet
7 | 20 | hax
7 | 30 | zor
How can I query it out in this manner:
id | first | second | third
---+-------+--------+--------
3 | hello | pls | respond
7 | leet | hax | zor

In the event that the num column does not reliably always start at 10 and ascend by 10 you can use the following to establish a row number that restarts at each change in ID, that way you can use the rownumbers in conjunction with conditional aggregation to show each comment. The following would do so for up to 10 comments per ID, and the NUM column does not have to be 10/20/30/40/50/60/70/80/90 (it could be anything).
If the NUM column reliably starts at 10 and ascends by 10, this question has been asked and answered: How to pivot rows into columns (custom pivoting)
select id,
max(case when row_number = 1 then comment else null end) as c01,
max(case when row_number = 2 then comment else null end) as c02,
max(case when row_number = 3 then comment else null end) as c03,
max(case when row_number = 4 then comment else null end) as c04,
max(case when row_number = 5 then comment else null end) as c05,
max(case when row_number = 6 then comment else null end) as c06,
max(case when row_number = 7 then comment else null end) as c07,
max(case when row_number = 8 then comment else null end) as c08,
max(case when row_number = 9 then comment else null end) as c09,
max(case when row_number = 10 then comment else null end) as c10
from(
select #row_number := case when #prev_val = id then #row_number+1 else 1 end as row_number,
id,
comment,
#prev_val:=id as prev_val
from tbl, (select #row_number:=0,#prev_val:='') x
order by id, num) x
group by id
order by id

Related

How to Query Maximum Integer Value from Substring of Varchar Column with Condition

I want to get the total maximum number of column CODE which the maximum is defined by the last five digits from mybarcode column.
mybarcode | code | judge | create_date |
-------------+------+--------+-------------+
M71X400001 | 7 | pass |
M71X400002 | 7 | pass |
M71X400005 | 7 | pass |
M71X400010 | 7 | pass |
M81X400001 | 8 | pass |
M81X400002 | 8 | pass |
M81X400007 | 8 | pass |
M91X400001 | 9 | pass |
M91X400003 | 9 | pass |
```
Example:
>The maximum value of 7 from CODE column is 10 ( from M71X4'00010')
>The maximum value of 8 from CODE column is 7 ( from M81X4'00007')
>The maximum value of 9 from CODE column is 3 ( from M91X4'00003')
The result should be 10+7+3=20.
And want display in the result table below.
```
SELECT DAY,
SUM(CASE WHEN judge = 'pass' then 1 else 0 end) pass,
SUM(CASE WHEN judge = 'fail' then 1 else 0 end) fail
**??? as number**
from MYTABLE
where MONTH(create_date) = '04' and YEAR(create_date) = '2019'
GROUP BY DAY
Result Table
day | pass | fail | number |
--------+------+--------+----------+
1 | 9 | 0 | 20 |
2 | 9 | 0 | ?? |
3 | 9 | 0 | ?? |
I think you need to do group by two times. Please try below code -
For MySQL -
SELECT
DAY,
SUM(pass),
SUM(fail),
SUM(max_barcode)
FROM (
SELECT
DAY,
SUM(CASE WHEN judge = 'pass' then 1 else 0 end) pass,
SUM(CASE WHEN judge = 'fail' then 1 else 0 end) fail,
Code,
CAST(MAX(SUBSTRING(mybarcode, 5)) AS SIGNED) AS max_barcode
FROM MYTABLE
WHERE MONTH(create_date) = '%s' and YEAR(create_date) = '%s'
GROUP BY DAY, Code
) AS CTE
GROUP BY DAY;
FOR MS SQL Server -
;WITH CTE AS (
SELECT
DAY,
SUM(CASE WHEN judge = 'pass' then 1 else 0 end) pass,
SUM(CASE WHEN judge = 'fail' then 1 else 0 end) fail,
Code,
max_barcode = cast(max(right(mybarcode, 5)) as int)
FROM MYTABLE
WHERE MONTH(create_date) = '%s' and YEAR(create_date) = '%s'
GROUP BY DAY, Code
)
SELECT
DAY,
SUM(pass),
SUM(fail),
SUM(max_barcode)
FROM CTE
GROUP BY DAY;

join mysql select queries as 1 then group by

Using information of this site I have been able to do the join but im having the issues doing the group by
is there a way to get the below statements to run as 1
Query1
SELECT count(location),date
from `filter`
where location != "red"
group by date
Query2
SELECT count(location),date
from `filter`
where location = "red"
group by date
I did try the below but it outputs the wrong data
Query3
SELECT
date,
(select count(location) from `filter` where location != "red") AS indoor,
(select count(location) from `filter` where location = "red") AS outdoor
from `filter` group by date;
SQL Fiddle for each query
http://sqlfiddle.com/#!9/17ebea/4 (query1)
http://sqlfiddle.com/#!9/17ebea/6 (query2)
http://sqlfiddle.com/#!9/90c945/1 (query3)
SELECT
date,
COUNT(CASE WHEN location <> 'red' THEN location ELSE NULL END) AS indoor,
COUNT(CASE WHEN location = 'red' THEN location ELSE NULL END) AS outdoor
FROM filter
GROUP BY date;
https://dev.mysql.com/doc/refman/8.0/en/control-flow-functions.html#operator_case
You can do conditional aggregation using CASE.. WHEN expressions.
SELECT
date,
COUNT(CASE WHEN location = 'red' THEN location END) AS outdoor,
COUNT(CASE WHEN location <> 'red' THEN location END) AS indoor
FROM `filter`
GROUP BY date;
View on DB Fiddle
Result
| date | outdoor | indoor |
| ---------- | ------- | ------ |
| 2018-11-14 | 1 | 4 |
| 2018-11-15 | 1 | 0 |
| 2018-11-16 | 0 | 3 |
| 2018-11-17 | 1 | 1 |
| 2018-11-18 | 0 | 1 |
| 2018-11-19 | 0 | 2 |
| 2018-11-20 | 0 | 1 |
You can also use the following other variants, like using COUNT(1) instead, or using SUM(..) function.
Alternative #1
SELECT
date,
COUNT(CASE WHEN location = 'red' THEN 1 END) AS outdoor,
COUNT(CASE WHEN location <> 'red' THEN 1 END) AS indoor
FROM `filter`
GROUP BY date;
Alternative #2
SELECT
date,
SUM(CASE WHEN location = 'red' THEN 1 ELSE 0 END) AS outdoor,
SUM(CASE WHEN location <> 'red' THEN 1 ELSE 0 END) AS indoor
FROM `filter`
GROUP BY date;
In MySQL, I would use the shortcut that allows you to sum() boolean variables:
select date, sum(location = 'red') as red,
sum(location <> 'red') as not_red
from filter
group by date ;
Notes:
Use single quotes for string and date constants -- not double quotes. Single quotes are the standard delimiter.
<> is the SQL inequality operator, although != is also supported by most databases.
This does not count NULL values.
To handle NULL values, you might want:
sum(not location <=> 'red') as not_red

Swapping rows to columns

I've read previous posts regarding pivot table and swapping rows to columns, but I couldn't find correct answer for my question. I have below table in MySQL:
+--------+--------+
| Userid | gname |
+--------+--------+
| 12 | AVBD |
| 12 | ASD |
| 12 | AVFD |
| 12 | Aew1 |
| 12 | AVBD32 |
| 12 | ASD23 |
| 12 | AVBDe |
| 12 | ASDer |
| 45 | AVBD |
| 45 | ASD444 |
| 45 | AVBD44 |
| 45 | ASD44 |
| 453 | AVBD22 |
| 453 | ASD1 |
+--------+--------+
I want to produce an ordered pivot table:
+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| Userid | gname1 | gname2 | gname3 | gname4 | gname5 | gname6 | gname7 | gname8 |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| 12 | AVBD | ASD | AVFD | Aew1 | AVBD32 | ASD23 | AVBDe | ASDer |
| 45 | AVBD | ASD444 | AVBD44 | ASD44 | | | | |
| 453 | AVBD22 | ASD1 | | | | | | |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+
the name of gname is dynamic and has no limit.
Here is the data setup along with a SQLFiddle http://sqlfiddle.com/#!2/65fec
CREATE TABLE gnames
(`Userid` int, `gname` varchar(6))
;
INSERT INTO gnames
(`Userid`, `gname`)
VALUES
(12, 'AVBD'),
(12, 'ASD'),
(12, 'AVFD'),
(12, 'Aew1'),
(12, 'AVBD32'),
(12, 'ASD23'),
(12, 'AVBDe'),
(12, 'ASDer'),
(45, 'AVBD'),
(45, 'ASD444'),
(45, 'AVBD44'),
(45, 'ASD44'),
(453, 'AVBD22'),
(453, 'ASD1')
;
This would be significantly easier if MySQL supported windowing functions because it's not necessarily the easiest to create a row number for each userid. I'll show you two ways to do this with hard-coded queries (limited number of results) and then I'll include one version that is using dynamic SQL.
In order to get the final result, you need some sequential number for each gname within the userid. This can be done a few different ways.
First, you can use a correlated subquery to count the number of gnames per user, you'd then use this sequence to create your new columns via an aggregate function with CASE expression:
select
userid,
max(case when gnameNum = 1 then gname else '' end) gname1,
max(case when gnameNum = 2 then gname else '' end) gname2,
max(case when gnameNum = 3 then gname else '' end) gname3,
max(case when gnameNum = 4 then gname else '' end) gname4,
max(case when gnameNum = 5 then gname else '' end) gname5,
max(case when gnameNum = 6 then gname else '' end) gname6,
max(case when gnameNum = 7 then gname else '' end) gname7,
max(case when gnameNum = 8 then gname else '' end) gname8
from
(
select userid,
gname,
(select count(*)
from gnames d
where g.userid = d.userid
and g.gname <= d.gname) as gnameNum
from gnames g
) src
group by userid;
See SQL Fiddle with Demo. Inside the subquery you are creating a row number for each gname, you then use this new value in the column creation. The problem with correlated subqueries is you could suffer from performance issues on larger data sets.
A second method would be to include user variables to create the row number. This code uses 2 variables to compare the previous row to the current row and increases the row number, if the userid is the same as the previous row. Again, you'd use the row number created to convert the data to new columns:
select
userid,
max(case when rownum = 1 then gname else '' end) gname1,
max(case when rownum = 2 then gname else '' end) gname2,
max(case when rownum = 3 then gname else '' end) gname3,
max(case when rownum = 4 then gname else '' end) gname4,
max(case when rownum = 5 then gname else '' end) gname5,
max(case when rownum = 6 then gname else '' end) gname6,
max(case when rownum = 7 then gname else '' end) gname7,
max(case when rownum = 8 then gname else '' end) gname8
from
(
select
g.userid,
g.gname,
#row:=case when #prev=userid then #row else 0 end + 1 as rownum,
#prev:=userid
from gnames g
cross join
(
select #row:=0, #prev:=null
) r
order by userid, gname
) src
group by userid;
See SQL Fiddle with Demo.
Now, in order to do this dynamically you will need to use a prepared statement. This process will create a sql string that you'll execute to get the final result. For this example, I used the user variable query above:
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'max(case when rownum = ',
rownum,
' then gname else '''' end) AS `gname',
rownum, '`'
)
) INTO #sql
from
(
select
g.userid,
g.gname,
#row:=case when #prev=userid then #row else 0 end + 1 as rownum,
#prev:=userid
from gnames g
cross join
(
select #row:=0, #prev:=null
) r
order by userid, gname
) src;
SET #sql = CONCAT('SELECT userid, ', #sql, '
from
(
select
g.userid,
g.gname,
#row:=case when #prev=userid then #row else 0 end + 1 as rownum,
#prev:=userid
from gnames g
cross join
(
select #row:=0, #prev:=null
) r
order by userid, gname
) src
group by userid');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
See SQL Fiddle with Demo. All three versions will give a result of:
| USERID | GNAME1 | GNAME2 | GNAME3 | GNAME4 | GNAME5 | GNAME6 | GNAME7 | GNAME8 |
|--------|--------|--------|--------|--------|--------|--------|--------|--------|
| 12 | Aew1 | ASD | ASD23 | ASDer | AVBD | AVBD32 | AVBDe | AVFD |
| 45 | ASD44 | ASD444 | AVBD | AVBD44 | | | | |
| 453 | ASD1 | AVBD22 | | | | | | |
One thing to consider when using the dynamic SQL is MySQL has a set length for the group_concat_max_len, if you have a lot of columns being created you might run into issues. You'll want to account for that. Here is another question that deals with this MySQL and GROUP_CONCAT() maximum length.

Combining data in SQL Union syntax

I'm doing a SQL Union syntax and I wanted to have a result like this:
+---------------+-----+---------+
|trkBusinessUnit| New | Pending |
+---------------+-----+---------+
| AIIB 2 0 |
| Credit Control 1 3 |
| Direct Center 1 2 |
| Financial Ins 1 1 |
| Motor Acclaim 1 0 |
+-------------------------------+
from my code:
SELECT trkBusinessUnit, Count(*) as New,0 as Pending
FROM tblDTPTracker
WHERE trkStatus = 'New'
GROUP BY trkBusinessUnit
UNION
SELECT trkBusinessUnit,0 as New,Count(*) as Pending
FROM tblDTPTracker
WHERE trkStatus = 'Pending'
GROUP BY trkBusinessUnit
but then the current output is:
+---------------+-----+---------+
|trkBusinessUnit| New | Pending |
+---------------+-----+---------+
| AIIB 2 0 |
| Credit Control 1 0 |
| Credit Control 0 3 |
| Direct Center 1 0 |
| Direct Center 0 2 |
| Financial Ins 1 0 |
| Financial Ins 0 1 |
| Motor Acclaim 1 0 |
+-------------------------------+
Am I missing out something or doing something wrong? Kindly advise.
There are (or has been) some syntax issues in the previous answers, but the intent of both earlier answers is correct, you need to use a GROUP BY query and NOT use UNION - which simply does not do what you were hoping/expecting.
UNION or UNION ALL work ROW by ROW, and absolutely do NOT merge by COLUMN
So, the MySQL syntax for the group by based query could be any of these:
COUNT() using an implicit NULL
SELECT
trkBusinessUnit
, COUNT(CASE WHEN trkStatus = 'New' THEN 1 END) as New
, COUNT(CASE WHEN trkStatus = 'Pending' THEN 1 END) as Pending
FROM tblDTPTracker
GROUP BY trkBusinessUnit
;
COUNT() using explicit NULL
SELECT
trkBusinessUnit
, COUNT(CASE WHEN trkStatus = 'New' THEN 1 ELSE NULL END) as New
, COUNT(CASE WHEN trkStatus = 'Pending' THEN 1 ELSE NULL END) as Pending
FROM tblDTPTracker
GROUP BY trkBusinessUnit
;
SUM() as an alternative to counting:
select
trkBusinessUnit
, sum(case when trkStatus = 'New' then 1 else 0 end) as New
, sum(case when trkStatus = 'Pending' then 1 else 0 end) as Pending
from tblDTPTracker
where trkStatus in ('Pending', 'New')
group by trkBusinessUnit
;
Apologies to both Marc Gravell & Daniel Gadawski who preceded this answer; this answer is a derivative of yours.
See this SQLFiddle demo of these queries
If I understand you correctly, you don't have to use an union.
Try:
SELECT
trkBusinessUnit,
COUNT(CASE WHEN trkStatus = 'New' THEN 1 ELSE NULL END) as New,
COUNT(CASE WHEN trkStatus = 'Pending' THEN 1 ELSE NULL END) as Pending
FROM tblDTPTracker
GROUP BY trkBusinessUnit
Union appends rows vertically (noting that UNION also takes only the distinct rows; UNION ALL takes all rows). What you want is either a full outer join of two sub-queries, or a more complicated select. I'd go with the latter!
select tblDTPTracker,
sum(case trkStatus when 'New' then 1 else 0 end) as New,
sum(case trkStatus when 'Pending' then 1 else 0 end) as Pending
from tblDTPTracker
where trkStatus in ('Pending', 'New')
group by tblDTPTracker
The full outer join approach would be something like:
SELECT ISNULL(x.trkBusinessUnit, y.trkBusinessUnit) as trkBusinessUnit,
ISNULL(x.New, 0) as New,
ISNULL(y.Pending, 0) as Pending
FROM (
SELECT trkBusinessUnit, Count(1) as New
FROM tblDTPTracker
WHERE trkStatus = 'New'
GROUP BY trkBusinessUnit) x
FULL OUTER JOIN (
SELECT trkBusinessUnit, Count(1) as Pending
FROM tblDTPTracker
WHERE trkStatus = 'Pending'
GROUP BY trkBusinessUnit) y on y.trkBusinessUnit = x.trkBusinessUnit

combining multiple different sql into one

cusID | Name | status | Date
---------------------------------
1 | AA | 0 | 2013-01-25
2 | BB | 1 | 2013-01-23
3 | CC | 1 | 2013-01-20
SELECT COUNT(cusID) FROM customer WHERE STATUS=0;
SELECT COUNT(cusID) FROM customer WHERE STATUS=1;
Is there a way of combing such two sql and return the results as one. Because want to avoid calling to DB everytime. I tried UNION of two statments, but only showing one result.
This is the shortest possible solution in MySQL.
SELECT SUM(status = 1) totalActive,
SUM(status = 0) totalInactive
FROM tableName
SQLFiddle Demo
and this is the CASE version
SELECT SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) totalActive,
SUM(CASE WHEN status = 0 THEN 1 ELSE 0 END) totalInactive
FROM tableName
SQLFiddle Demo