Dynamically transpose rows into columns in MySQL - mysql

I have a simple entity attribut value table with 3 columns 1) policyholder_locator; 2) field_name; 3) field_value. Sample data as follows:
policyholder_locator,field_name,field_value
1,first_name,john
1,phone,8888888888
1,email,john#gmail.com
2,first_name,jane
2,phone,7777777777
2,email,jane#gmail.com
3,first_name,joe
3,phone,6666666666
3,email,joe#gmail.com
I would ideally like to use the following GROUP_CONCAT and prepared statements code, but I keep getting the following error when running "An SQLException was provoked by the following failure: java.lang.ArrayIndexOutOfBoundsException".
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'MAX(CASE WHEN field_name = ''',
field_name,
''' THEN field_value END) ',
field_name
)
) INTO #sql
FROM policyholder_fields;
SET #sql = CONCAT('SELECT policyholder_locator, ',#sql, '
FROM policyholder_fields
GROUP BY policyholder_locator');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
I could go with code that's not dynamic like below, but would prefer a dynamic option.
SELECT policyholder_locator,
GROUP_CONCAT(IF(field_name = 'phone', field_value, NULL)) AS phone,
GROUP_CONCAT(IF(field_name = 'email', field_value, NULL)) AS email,
GROUP_CONCAT(IF(field_name = 'first_name', field_value, NULL)) AS first_name
FROM policyholder_fields
GROUP BY policyholder_locator
Is there an obvious issue with my prepared statements?

Related

Dynamic pivot query using MySQL

I have the following table with the details:
Example:
CREATE TABLE Table1
(`PK` int, `Name` varchar(3), `Subject` varchar(9), `Grade` varchar(1));
INSERT INTO Table1
(`PK`, `Name`, `Subject`, `Grade`)
VALUES
(1, 'Bob', 'Math', 'A'),
(2, 'Bob', 'History', 'B'),
(3, 'Bob', 'Language', 'C'),
(4, 'Bob', 'Biology', 'D'),
(5, 'Sue', 'History', 'C'),
(6, 'Sue', 'Math', 'A'),
(7, 'Sue', 'Music', 'A'),
(8, 'Sue', 'Geography', 'C');
Now I want to write a store procedure through which I want to pivot the table.
Attempt
DELIMITER $$
create PROCEDURE sptest1(IN nm varchar(50),IN sub varchar(50))
begin
SET #sql = NULL;
SELECT GROUP_CONCAT(DISTINCT
CONCAT('MAX(CASE WHEN ', nm ,' = ''', nm,
''' THEN grade END) `', nm, '`'))
INTO #sql
FROM table1;
SET #sql = CONCAT('SELECT ', sub ,',', #sql, '
FROM table1
GROUP BY ', sub ,'');
select #sql;
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END$$
DELIMITER ;
Calling Function:
call sptest1('name','subject')
But getting wrong result:
subject name
----------------
Biology null
Geography null
History null
Language null
Math null
Music null
Expected Result:
subject Bob Sue
-------------------------
Biology D null
Geography null C
History B C
Language C null
Math A A
Music null A
You can rewrite you dynamic sql query as
SET #sql = NULL;
SELECT GROUP_CONCAT( DISTINCT
CONCAT('MAX(CASE WHEN `Name` = ''',
`Name`,
''' THEN grade END) ',
`Name`)
)
INTO #sql
FROM table1;
SET #sql = CONCAT('SELECT `Subject`, ', #sql, ' FROM table1 GROUP BY `Subject`');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Demo
In your stored procedure you have to hard code your name column for you dynamic sql if you pass it as parameter then you can use case to check for column, for example
MAX(CASE WHEN ', nm ,' = ''', nm, ..
Above part of procedure will evaluated as MAX(CASE WHEN name = 'name' .. because variable nm contains 'name' it will not recognize it as column
DROP PROCEDURE `sptest1`;
DELIMITER $$
CREATE
PROCEDURE `sib`.`sptest1`(IN nm VARCHAR(50),IN sub VARCHAR(50))
BEGIN
SET #sql = NULL;
SELECT GROUP_CONCAT( DISTINCT
CONCAT('MAX(CASE WHEN ',nm,' = ''',CASE WHEN nm = 'name' THEN `Name` END,''' THEN grade END) ',CASE WHEN nm = 'name' THEN `Name` END)
)
INTO #sql
FROM table1;
SET #sql = CONCAT('SELECT ', sub ,',', #sql, ' FROM table1 GROUP BY ', sub ,'');
/*select #sql; */
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END$$
DELIMITER ;
Demo

MySQL SELECT and GROUPING

I am new to SQL (mySQL) and I am trying to create a query that will populate a google chart specifically
The result should look something like this:
['group_name', 'Open_tickets_count', 'closed_tickets_count'],
['Team A', 10, 400],
['Team B', 30, 460],
['Team C', 66, 1120],
['Team D', 5, 540]
I have a tickets table that includes the following fields: id ticket_status group_name.
A few things:
In this example I only have two statuses, but the number of
statuses is actually variable
The number of teams is also variable
the actual group_names and tickets_statuses are ids to other
tables in my real table. (but I can figure out how to substitute)
I have tried everything my imagination and limited knowledge allows and got not even near to achieve this. Any help or pointers would be highly appreciated.
Thank you.
[Adding sample table structures (only relevant fields)]
Table: tickets
'id', 'int(10)'
'ticket_status_id', 'int(10)'
'ticket_group_id', 'int(10)'
Table: status
'id', 'int(10)'
'name', 'varchar(250)'
Table: groups
'id', 'int(10)'
'name', 'varchar(250)'
In order to achieve your goal you need to use conditional SUM() with GROUP BY and dynamic SQL (PREPARE, EXECUTE).
Your base script might look like this
SET #sql = '';
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'SUM(CASE WHEN t.ticket_status = ''',
id,
''' THEN 1 ELSE 0 END) `',
name, '`'
)
) INTO #sql
FROM
status;
SET #sql = CONCAT
('SELECT g.name, ', #sql, '
FROM tickets t JOIN status s
ON t.ticket_status = s.id JOIN groups g
ON t.ticket_group_id = g.id
GROUP BY t.ticket_group_id, g.name'
);
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Here is SQLFiddle demo.
Lets go with your assumptions.
You can try something like
SELECT group_name,
SUM(CASE WHEN ts.status = 'OPEN' THEN 1 ELSE 0 END) Open_tickets_count
SUM(CASE WHEN ts.status = 'CLOSED' THEN 1 ELSE 0 END) closed_tickets_count
FROM tickets t INNER JOIN
ticket_statusses ts ON t.ticket_status = ts.id
GROUP BY group_name
You would have to add new columns for different statusses.

MySQL sum results into a table grid format

I have the following SQL code:
select distinct members_main.membership_type,
payment_method,sum(amount_paid)
from members_main, members_payments
where members_main.contact_id=members_payments.contact_id
group by members_main.membership_type,payment_method
That returns the following:
I want to be able to return the results in the following table grid format:
Does anyone know if or how I can do this within MySQL?
Thanks,
John
Depending on data complexity either go with bluefeet's way or if the payment_method amount is dynamic then it sounds like you need a pivot table. Try looking at this.
MySQL does not have a pivot function but you can use a CASE expression and an aggregate function to turn the rows into columns:
select m.membership_type,
sum(case when p.payment_method = 'Cash' then amount_paid else 0 end) Cash,
sum(case when p.payment_method = 'Credit Card' then amount_paid else 0 end) CreditCard,
sum(case when p.payment_method = 'Paypal' then amount_paid else 0 end) Paypal
from members_main m
inner join members_payments p
on m.contact_id=p.contact_id
group by m.membership_type;
If you are going to have an unknown number of payment_method's, then you will want to look at using a prepared statement to generate dynamic SQL:
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'sum(CASE WHEN p.payment_method = ',
payment_method,
' THEN amount_paid else 0 END) AS `',
payment_method, '`'
)
) INTO #sql
FROM members_payments ;
SET #sql
= CONCAT('SELECT m.membership_type, ', #sql, '
from members_main m
inner join members_payments p
on m.contact_id=p.contact_id
group by m.membership_type');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

How to get the output in mysql the way i need is give below

i have one table with a single column having three values a,b and c.
Current snapshot of the table is
Table name:- tblTest
Values
tblColumn
a
a
a
b
b
b
b
c
c
i need to get the output exactly as
A B C
3 4 2
select sum(tblColumn = 'a') as A,
sum(tblColumn = 'b') as B,
sum(tblColumn = 'b') as C
from tblTest
SQL Fiddle Example
SELECT SUM(CASE WHEN colName = 'a' THEN 1 ELSE 0 END) as A,
SUM(CASE WHEN colName = 'b' THEN 1 ELSE 0 END) as B,
SUM(CASE WHEN colName = 'c' THEN 1 ELSE 0 END) as C
FROM tableName
another technique is by using PreparedStatement, this is very good if you have multiple unknown number group of values, eg, a,b,c,d,e,f,g,h,...
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'SUM(IF(tblColumn = ''',
tblColumn,
''', 1, 0)) AS ',
tblColumn
)
) INTO #sql
FROM
Table1;
SET #sql = CONCAT('SELECT ', #sql, ' FROM Table1');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SQLFiddle Demo
Try this
select tblColumn, Count(*) from tblTest group by tblColumn

how to display data in mysql like pivot in excel

I have query like this:
SELECT DATE,REGION,COUNT(*)
FROM ALL_ID_DATA
WHERE DATE in (SUBDATE(CURDATE(),1),SUBDATE(CURDATE(),2),SUBDATE(CURDATE(),8))
AND DIRECTION='inbound'
AND REASON_1 = 'complaint'
GROUP BY REGION,DATE DESC
and the result is
What is the right query to display like this capture below
Could somebody help me?
This type of query is known as a PIVOT. Unfortunately, MySQL doesn't have a PIVOT function, so you will need to replicate the function using a CASE statement and an aggregate function.
If you know the number of columns to transform, then you can hard-code the values:
select region,
SUM(CASE WHEN date = '2012-09-24' THEN 1 END) as `2012-09-24`,
SUM(CASE WHEN date = '2012-09-30' THEN 1 END) as `2012-09-30`,
SUM(CASE WHEN date = '2012-10-01' THEN 1 END) as `2012-10-01`
from ALL_ID_DATA
group by region;
select region,
COUNT(CASE WHEN date = '2012-09-24' THEN 1 ELSE null END) as `2012-09-24`,
COUNT(CASE WHEN date = '2012-09-30' THEN 1 ELSE null END) as `2012-09-30`,
COUNT(CASE WHEN date = '2012-10-01' THEN 1 ELSE null END) as `2012-10-01`
from ALL_ID_DATA
group by region;
See SQL Fiddle with Demo.
Then adding this to your existing query, it would be:
SELECT REGION,
SUM(CASE WHEN date = '2012-09-24' THEN 1 END) as `2012-09-24`,
SUM(CASE WHEN date = '2012-09-30' THEN 1 END) as `2012-09-30`,
SUM(CASE WHEN date = '2012-10-01' THEN 1 END) as `2012-10-01`
FROM ALL_ID_DATA
WHERE DATE in (SUBDATE(CURDATE(),1),SUBDATE(CURDATE(),2),SUBDATE(CURDATE(),8))
AND DIRECTION='inbound'
AND REASON_1 = 'complaint'
GROUP BY REGION
OR
SELECT REGION,
COUNT(CASE WHEN date = '2012-09-24' THEN 1 ELSE null END) as `2012-09-24`,
COUNT(CASE WHEN date = '2012-09-30' THEN 1 ELSE null END) as `2012-09-30`,
COUNT(CASE WHEN date = '2012-10-01' THEN 1 ELSE null END) as `2012-10-01`
FROM ALL_ID_DATA
WHERE DATE in (SUBDATE(CURDATE(),1),SUBDATE(CURDATE(),2),SUBDATE(CURDATE(),8))
AND DIRECTION='inbound'
AND REASON_1 = 'complaint'
GROUP BY REGION
Now if you have an unknown number of dates to transform into columns, then you can use prepared statements and your query would be similar to this (See SQL Fiddle with Demo):
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'sum(case when date = ''',
date,
''' then 1 else 0 end) AS ''',
Date(date), ''''
)
) INTO #sql
FROM ALL_ID_DATA;
select #sql;
SET #sql = CONCAT('SELECT region, ', #sql, '
FROM ALL_ID_DATA
GROUP BY region');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Then placing your original query in the prepared statement the final query would be:
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'sum(case when date = ''',
date,
''' then 1 else 0 end) AS ''',
Date(date), ''''
)
) INTO #sql
FROM ALL_ID_DATA;
select #sql;
SET #sql = CONCAT('SELECT region, ', #sql, '
FROM ALL_ID_DATA
WHERE DATE in (SUBDATE(CURDATE(),1),SUBDATE(CURDATE(),2),SUBDATE(CURDATE(),8))
AND DIRECTION=''inbound''
AND REASON_1 = ''complaint''');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
I think you are looking for this :
http://www.artfulsoftware.com/infotree/queries.php#78