I have three tables in MySQL: application, questions, and questions_answer.
application stores user_id; questions stores question_id and the type of questions (i.e Name, NRIC, Name of School); questions_answer stores the answers referencing to user_id and question_id.
From what I understand, this type of association is called polymorphic association. Now I am at lost of how to retrieve data from different question_id and make them as column header instead.
I hope that makes sense.
Edit:
To illustrate, here are the respective table:
application:
user_id name
-------------------------------
100 Leon Barnacles
101 Richard Kennard
102 Fareeza Salleh
questions:
question_id question_name
---------------------------------------------
20 NRIC
21 Have you ever applied to TFM?
22 What's your current GPA?
23 Name of school
questions_answer:
question_id user_id answer
------------------------------------------------
20 100 880808-06-8990
20 100 900990-14-0911
23 102 SMK Taman Pandamaran
What I hope to retrieve:
Name NRIC Name of school
------------------------------------------------------------
Leon Barnacles 880808-06-8990
Richard Kennard 900990-14-0911
Fareeza Salleh SMK Taman Pandamaran
What you need is a PIVOT type function, MYSQL doesn't support PIVOT like SQL Server or Oracle. You can use following script to achieve pivot using max and case on questions table data
select group_concat(concat('max(case when question_id = ''', question_id, ''' then
answer end) as `', question_name ,'`')) into #sql from tfm_questions;
set #sql =
concat('select full_name, ', #sql, ' from
(
select a.full_name, q.question_name, an.answer, q.question_id
from tfm_application a
inner join tfm_questions_answer an on a.user_id = an.user_id
inner join tfm_questions q on an.question_id = q.question_id
) x
group by full_name');
select #sql;
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SQL DEMO
Related
I have arranged a survey where a project has some questions. Users have to collect the given project answer from public. Survey tables like
user tables
user_id user_name
1 User 1
2 User 2
Project table
project_id project_name
1 project 1
2 project 2
Question table
ques_id project_id ques_name
1 1 Question 1
2 1 Question 2
3 1 Question 3
4 1 Question 4
Answer table
ans_id public_id user_id ques_id ques_ans
1 1 1 1 Answer 1
2 1 1 2 Answer 2
3 1 1 3 Answer 3
4 1 1 4 Answer 4
Now i want to generate a reports where question table values as column name matched by given project_id and question answers as value from answer table matched by ques_id
And, her is my expected output:
User_Name public_id Question 1 Question 2 Question 3 ...
User 1 1 Answer 1 Answer 2 Answer 3 ...
Someone suggested to use pivot but i found "MySQL doesn't have native support for pivoting operations" can anyone help me out?
You can use another output format of the query. For example:
SELECT user_name, answer.project_id, ques_name, ques_ans
FROM
`answer`
INNER JOIN `user` USING (user_id)
INNER JOIN `question` USING (ques_id);
To restrict rows by specific project add WHERE clause:
WHERE project_id = #ProjectID
Then transform the result to the desired view using PHP.
If it is critical to solve the question using MySQL then create new colums manually using aliaces. To aggregate rows by user and project use GROUP BY clause. To show the possible non-empty values use MAX() function. In your case:
SELECT
user_name, project_id,
MAX(IF(ques_name = 'Question 1', ques_ans, NULL)) AS `Question 1`,
MAX(IF(ques_name = 'Question 2', ques_ans, NULL)) AS `Question 2`,
MAX(IF(ques_name = 'Question 3', ques_ans, NULL)) AS `Question 3`,
MAX(IF(ques_name = 'Question 4', ques_ans, NULL)) AS `Question 4`
FROM
(SELECT
ans_id, user_id, user_name, answer.project_id, ques_name, ques_ans
FROM
answer
INNER JOIN `user` USING (user_id)
INNER JOIN question USING (ques_id)
) AS tmp
GROUP BY
user_id, project_id;
Finally the code is working
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'MAX(IF(a.ques_id = ',
ques_id,
', a.ques_ans, NULL)) AS `',
ques_name,'`'
)
) INTO #sql
FROM survey_answer inner join survey_question on survey_answer.ques_id=survey_question.id;
set #sql = CONCAT('select u.user_name ,q.category_id,a.p_code, ' ,#sql,' FROM `survey_answer` as a
LEFT JOIN `users` as u ON a.user_id = u.user_id
LEFT JOIN `survey_question` as q ON a.ques_id= q.id');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
your query should be :
SELECT an.user_name,an.public_id,qs.quest_name,an.quest_answer
FROM answer_table an,question_table qs,user_table usr
WHERE an.quest_id = qs.quest_id
AND an.user_id=usr.user_id
For the table rotation : here a similar question about rotating tables
mysql pivot table date (vertical to horizontal data)
Similar to MySQL Group By values that are equal over multiple columns
How do I transform the mysql database table below to the output using MySQL:
id entity_id volunteer_1 volunteer_2 volunteer_3
1 1540 1933 1253 2543
2 1638 2543 2123 1736
3 1736 1253 1933 2123
4 1834 1525 1253 2123
5 1932 2123 2543 2451
6 2030 2854 2451 1933
7 2128 2451 2854 2543
To the output
Jan-15 Feb-16 Mar-16
Actual Name 1 3 4 6
Actual Name 2 3 3 5
Actual Name 3 1 5 6
Things to note:
Date time is determined by entity_id field linking to an activity.activity_date_time
Each volunteer ID should output actual name by linking to contact.first_name and contact.last_name of the ID in the field
Solved by doing a union first, to get the data per the suggested answer (single column format). The suggested answer doesn't consolidate similar data from multiple columns. Then you can work with the temporary results and transform as required. Further, it has to be a stored procedure, to the statement won't execute with results.
DELIMITER $$
DROP PROCEDURE IF EXISTS `civicrm_report_tmp_volunteer`$$
CREATE PROCEDURE `civicrm_report_tmp_volunteer`()
BEGIN
DROP TABLE IF EXISTS civicrm_report_tmp_volunteer_hours;
SET #group_concat_max_len = 5000;
CREATE TEMPORARY TABLE civicrm_report_tmp_volunteer_hours AS
SELECT
CONCAT(c.first_name, ' ' , c.last_name) as contact_name,
a.contact_id as contact_id,
EXTRACT( YEAR_MONTH FROM ca.activity_date_time ) as sort_date,
CONCAT(
MONTHNAME(STR_TO_DATE(EXTRACT( MONTH FROM ca.activity_date_time), '%m')),
"-",
EXTRACT( YEAR FROM ca.activity_date_time )) as month_year,
-- a.activity_id,
-- ca.activity_type_id,
-- ca.activity_date_time,
-- ca.duration,
SUM(ca.duration) as activity_duration,
COUNT(DISTINCT (a.activity_id)) activity_count
FROM
(
SELECT volunteer_1_543 as contact_id, entity_id as activity_id FROM civicrm_value_volunteer_details_103
WHERE volunteer_1_543 IS NOT NULL
UNION
SELECT volunteer_2_544 as contact_id, entity_id as activity_id FROM civicrm_value_volunteer_details_103
WHERE volunteer_2_544 IS NOT NULL
UNION
SELECT volunteer_3_545 as contact_id, entity_id as activity_id FROM civicrm_value_volunteer_details_103
WHERE volunteer_3_545 IS NOT NULL
UNION
SELECT volunteer_4_546 as contact_id, entity_id as activity_id FROM civicrm_value_volunteer_details_103
WHERE volunteer_4_546 IS NOT NULL
) as a
LEFT JOIN civicrm_activity ca
ON a.activity_id = ca.id
LEFT JOIN civicrm_contact c
ON c.id = a.contact_id
WHERE ca.activity_type_id IN (184)
GROUP BY a.contact_id, month_year;
SET #query = null;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'MAX(IF(sort_date = ''',
sort_date,
''', activity_duration, 0)) AS ',
"'", month_year, "' "
)
) INTO #query
FROM civicrm_report_tmp_volunteer_hours;
SET #query = CONCAT('SELECT contact_name, ', #query , ' FROM civicrm_report_tmp_volunteer_hours GROUP BY contact_id');
PREPARE stmt FROM #query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END$$
DELIMITER ;
CALL civicrm_report_tmp_volunteer();
Probably a very basic error but here is my problem.
There are users who are giving likes to certain pages of a given document and my aim is to return a breakdown of pages liked per user
Here are (a simplified view of) the two following tables:
User table
id name
---------
1 Jim
2 John
Vote table
userid pageno voteup
1 1 1
1 2 1
2 1 1
2 2 1
2 3 1
My desired output would be the following:
id name Page 1 Page 2 Page 3
1 Jim 1 1 0
2 John 1 1 1
I've made my prepared statement as followed. My aim is to display 'Page 1', 'Page 2' and so on for the column names instead of the 'test' below but as my pageno field is an int i fail in formatting the column name. I have tried various things but with no luck.
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'MAX(IF(v.pageno = ',
pageno,
', v.voteup, 0)) AS ',
'test'
)
) INTO #sql
FROM vote;
SET #sql = CONCAT('SELECT u.id, u.name, ', #sql, '
FROM user u
LEFT JOIN vote AS v
ON u.id = v.id
GROUP BY u.id');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
I'm sure this is something very basic I am missing. Could you help?
Thanks in advance
Link to SQLFiddle
This will do the trick although is not dynamic, if you need a dynamic solution.....
select
n.id,
n.name,
CASE WHEN v.pageno=1 THEN sum(v.voteup) ELSE NULL END as "Page 1"
CASE WHEN v.pageno=2 THEN sum(v.voteup) ELSE NULL END as "Page 2"
from votetable as v
join usertable as n on n.id = v.userid
i would like convert data from Table1 as you can see on first picture on data in Pivot_table. Below you can see script in Mysql that is for dynamic pivot table, but i would like to know similar or suitable solution for this in Postgresql, if it is possible?
Table1:
PK Name Subject Grade**
-------------------------------------
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
Pivot_table:
Subject Bob Sue
-------------------------
Math A A
History B C
Language C
Biology D
Music A
Geography C
Script in Mysql:
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;
Thank you very much
i have the following tables:
**entries**
entry_id | date | engineer | project
**entries_allowanes_map**
entry_id | allowance_id
**allowances**
allowance_id | allowance_name
I want to create a SELECT query that will give the following result:
entry_id | date | engineer | project | allowance_name1 | allowance_name2 | allowance_name_n...
The queries I have tried return a row for each allowance an entry has registered with. I want just one row with all allowances attached to it.
Thanks in advance
I would propose doing this with group_concat(). It doesn't put the values in separate columns, but it does put everything for a given entry on one row:
select e.entry_id, e.date, e.engineer, e.project,
group_concat(a.allowance_name) as allowances
from entries e join
entries_allowances_map f
on e.entry_id = eam.entry_id
allowances a
on eam.allowance_id = a.allowance_id
group by e.entry_id;
Here is the query that I got:
It outputs your expected results in different columns:
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'(SELECT max(CASE WHEN AL.ALLOWANCE_ID = ''',
ALLOWANCE_ID,
''' THEN 1 END) AS `',
ALLOWANCE_ID, '` FROM entries_allowanes_map AL WHERE E.ENTRY_ID = AL.ENTRY_ID ) AS `',
ALLOWANCE_NAME, '`'
)
) INTO #sql
FROM allowances;
SET #sql
= CONCAT('SELECT E.ENTRY_ID, E.DATE, E.ENGINEER, E.PROJECT, ', #sql, '
FROM entries as e');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Here is the SqlFiddle
Try
SELECT * FROM entries as e
INNER JOIN entries_allowanes_map as a ON e.entry_id=e.entry_id
INNER JOIN allownces as al ON al.allownce_id=a.allowance_id