I have seven tables (from XT1 to XT7) and each have the same structure as below:
Date
m1
m2
m3
2021-06-01
4
2
6
2021-06-02
3
2
5
2021-06-03
12
2
14
.....
..
..
..
I only need m3 value from each table. What I'm trying to do, is to show all m3 values in a single table and sum them up like below:
Date
XT1.m3
XT2.m3
XT3.m3
XT4.m3
XT5.m3
XT6.m3
XT7.m3
Subtotal XT1~7.m3
2021-06-01
6
7
8
6
7
8
8
50
2021-06-02
6
7
8
6
7
8
8
50
2021-06-03
6
7
8
6
7
8
8
50
Total
18
21
24
18
21
24
24
150
What I have tried before:
select (TX1.m3+TX2.m3+TX3.m3...) AS subtotal, date_format(date, '%Y-%m') as date,TX1.m3,TX2.m3,TX3.m3
from TX1 AS c1 left join TX2 AS c2 on TX1.date=TX2.date
left join TX3 AS c3 on TX2.date=TX3.date
Which was modified from a previous working script. But the old structure only had 3 tables and now I have seven.
UPDATE:Tried the following
SELECT TX1.date,TX1.m3,TX2.m3,TX2.m3...TX7.m3
FROM TX1
inner JOIN TX2 ON TX1.date = TX2.date
inner JOIN TX3 ON TX2.date = TX3.date
......
inner JOIN TX7 ON TX6.date = TX7.date
Above code will return an un-grouped matrix. Tried to group with 'date' and returned error 1055. Override with
SET sql_mode=(SELECT REPLACE(##sql_mode,'ONLY_FULL_GROUP_BY',''));
Now works, but not sure if this is the correct way to do. Any ideas?
If you want to use the same query structure with addition of newly created tables, you can try using prepared statement. For example:
Preparing field values:
/*setting variables as NULL*/
SET #sql = NULL;
SET #tbl = NULL;
SET #val1 = NULL;
SET #val2 = NULL;
SET #reftbl = 'TX1'; /*this is your reference (most left) table in the LEFT JOIN*/
/*setting each variables with values*/
SELECT GROUP_CONCAT(CONCAT(table_name,'.',column_name,' AS ',table_name,column_name) SEPARATOR ', ') INTO #val1
FROM information_schema.columns
WHERE table_name LIKE 'TX%'
#AND table_schema=your_database_name
AND column_name='m3';
SELECT CONCAT(GROUP_CONCAT(CONCAT(table_name,'.',column_name) ORDER BY table_name SEPARATOR '+' ),' as subtotal') INTO #val2
FROM information_schema.columns
WHERE table_name LIKE 'TX%'
#AND table_schema=your_database_name
AND column_name='m3'
Here we are using information_schema.columns tables to generate field of all the m3 columns coming from all tables that correspond to the condition (table_name and table_schema). This will return you something like:
#val1: TX1.m3 AS TX1m3, TX2.m3 AS TX2m3, TX3.m3 AS TX3m3, TX4.m3 AS TX4m3, TX5.m3 AS TX5m3, TX6.m3 AS TX6m3, TX7.m3 AS TX7m3
#val2:TX1.m3+TX2.m3+TX3.m3+TX4.m3+TX5.m3+TX6.m3+TX7.m3 as subtotal
Next is to prepare the tables required in the query and add LEFT JOIN.
SELECT CONCAT(#reftbl,
GROUP_CONCAT(
CASE WHEN tbn IS NOT NULL
THEN CONCAT(' LEFT JOIN ', table_name,' ON ',tbn,'.date =',table_name,'.date')
ELSE table_name END SEPARATOR ' ')) INTO #tbl
FROM
(SELECT #reftbl tbn,
table_name
FROM information_schema.tables
WHERE table_name LIKE 'TX%'
#AND table_schema=your_database_name
AND table_name <> #reftbl) v;
The operation I'm doing here is to generate the LEFT JOIN parts by using CONCAT and GROUP_CONCAT.
Then we set #sql variable with a query generated based on all the variables we previously set; constructed using CONCAT.
/*constructing query and set into #sql*/
SET #sql = CONCAT('SELECT DATE_FORMAT(TX1.DATE, "%Y-%m") AS DATE,',#val1,',',#val2,'
FROM ',#tbl,';');
This will end up with a query like:
SELECT DATE_FORMAT(TX1.DATE, "%Y-%m") AS DATE,TX1.m3 AS TX1m3, TX2.m3 AS TX2m3,
TX3.m3 AS TX3m3, TX4.m3 AS TX4m3, TX5.m3 AS TX5m3, TX6.m3 AS TX6m3, TX7.m3 AS TX7m3,
TX1.m3+TX2.m3+TX3.m3+TX4.m3+TX5.m3+TX6.m3+TX7.m3 as subtotal
FROM TX1 LEFT JOIN TX2 ON TX1.date =TX2.date
LEFT JOIN TX3 ON TX1.date =TX3.date
LEFT JOIN TX4 ON TX1.date =TX4.date
LEFT JOIN TX5 ON TX1.date =TX5.date
LEFT JOIN TX6 ON TX1.date =TX6.date
LEFT JOIN TX7 ON TX1.date =TX7.date;
Lastly, prepare, execute then deallocate the #sql statement and you'll get the desired result you're looking for:
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
This is a way of generating 'dynamic query' which means if/when you add/remove tables, the query will be generated with the additional or without the removed tables. For example, if there's a new table TX8, when you run the queries above, it will include TX8 in the prepared statement as long as it match the condition.
Here's a demo fiddle that includes a situation when there's a new table created.
In another database, this problem would be solved with multiple FULL joins, but since MySql does not support them and simulating them for 7 tables is not an option, I would choose UNION ALL.
From each table, return 8 columns: 1 for the date, 1 for m3 and 6 null columns.
Use UNION ALL to combine them and finally aggregate:
SELECT date,
MAX(m3_1) m3_1, MAX(m3_2) m3_2, MAX(m3_3) m3_3, MAX(m3_4) m3_4, MAX(m3_5) m3_5, MAX(m3_6) m3_6, MAX(m3_7) m3_7,
COALECSE(MAX(m3_1), 0) + COALECSE(MAX(m3_2), 0) + COALECSE(MAX(m3_3), 0) + COALECSE(MAX(m3_4), 0) +
COALECSE(MAX(m3_5), 0) + COALECSE(MAX(m3_6), 0) + COALECSE(MAX(m3_7), 0) subtotal
FROM (
SELECT date, m3 m3_1, null m3_2, null m3_3, null m3_4, null m3_5, null m3_6, null m3_7 FROM XT1
UNION ALL
SELECT date, null, m3, null, null, null, null, null FROM XT2
UNION ALL
SELECT date, null, null, m3, null, null, null, null FROM XT3
UNION ALL
............................................................
SELECT date, null, null, null, null, null, null, m3 FROM XT7
) t
GROUP BY date
Related
I need to put a record at last of the result set ordered by a column field in ascending order.
LIKE:
SELECT "#TOTAL"...
Which is a user-defined column value.
Here's what I am trying to do:
SELECT cnt.name as Client, NULL, NULL, NULL, ', #COL_SUM, '
FROM
task as tsk
LEFT JOIN client cnt
ON tsk.client_id = cnt.id
GROUP BY tsk.client_id
UNION ALL
SELECT "#TOTAL#",NULL,NULL,NULL, ', #COL_SUM, '
FROM task as tsk
ORDER BY Client ASC
This returns result set as:
Client | ... | Admin | Intern | ..
---------------------------------------
#TOTAL# | ... | 4 | 2 | .. <-- this row here is grand total
A | ... | 1 | | ..
B | ... | 1 | 1 | ..
C | ... | 2 | 1 | ..
I want the #TOTAL# row to be at last.
What character, except z, comes at last in alphabetical order?
Just add your condition to the ORDER BY clause:
order by Client = '#TOTAL#', Client
You could add another constant column to the individual queries that indicate if it's a total or a regular row and order by that first.
SELECT *
FROM (SELECT 0 first_order,
cnt.name client,
...
UNION ALL
SELECT 1 first_order,
'#TOTAL#' client,
...) x
ORDER BY first_order,
client;
Try this variant of code.
For mysql:
SELECT 0 as [OrderBy], cnt.name as Client, NULL, NULL, NULL, ', #COL_SUM, '
FROM
task as tsk
LEFT JOIN client cnt
ON tsk.client_id = cnt.id
GROUP BY tsk.client_id
UNION ALL
SELECT 1 , "#TOTAL#",NULL,NULL,NULL, ', #COL_SUM, '
FROM task as tsk
ORDER BY [ORDERBY], cLIENT_ID
For sql server
SELECT * FROM (
SELECT TOP 100 PERCENT cnt.name as Client, NULL, NULL, NULL, ', #COL_SUM, '
FROM
task as tsk
LEFT JOIN client cnt
ON tsk.client_id = cnt.id
GROUP BY tsk.client_id
ORDER BY TSK.CLIENT_ID) AS DATA
UNION ALL
SELECT "#TOTAL#",NULL,NULL,NULL, ', #COL_SUM, '
FROM task as tsk
Since you are not able to apply order by in the first part of your query, so there is a method to do so, top 100 percent, and you can apply order by clause in your inner query or sub query.
Hope this helps.
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();
please help me to answer this question..
if I have database, table name is table1 and the field name is example its contain 5 numbers like this:
example
1
2
3
4
5
then I would like do this with database above:
1*2 = 2
3*2 = 6
4*6 = 24
5*24 = 120
120 * 10% = 12
What is the correct sql query to make that happen?
thank you very much
You can use user-defined variables in you query ,if multiplication result exceeds from 100 and you need to divide it by 10 you can do so
SELECT
CONCAT(example,'*',#mul) `expression`,
#mul := CASE
WHEN #mul* example >100
THEN (#mul* example/10)
ELSE #mul* example END
mul
FROM Table1,
(SELECT #mul:=1) p
ORDER BY `example`
Fiddle Demo
For your exactly the same desired result set this would work
SELECT * FROM
(SELECT
CONCAT(example,'*',#mul) `expression`,
#mul := #mul* example mul
FROM Table1,
(SELECT #mul:=1) p
ORDER BY `example`
) t
UNION
SELECT CONCAT(#mul,'*',' 10% ') `expression`,
(#mul/10) FROM (SELECT #mul) p1
Fiddle Demo
Consider the following (1:N) relationship:
[entity: user] <------ rid key ------> [entity: rid].
consider the data in both tables as:
select * from user;
user-id rid-key
a-basa a
b-basa b
a.a-basa a.a
a.b-basa a.b
a.a.a-basa a.a.a
a.a.b-basa a.a.b
a.b.a-basa a.b.a
a.b.b-basa a.b.b
a.b.b.a-basa a.b.b.a
a.b.b.b-basa a.b.b.b
select * from rid;
rid-key parent-rid enabled
a null true
b null true
a.a a true
a.b a false
a.a.a a.a true
a.b.a a.b true
a.b.b a.b true
a.b.b.a a.b.b true
......
n rows
I need to design a single query (not stored procedure) which will input a user-id, and the following facts are considered:
If an user is given access to a rid, then it can also access the parent rid of the rid given - the rid itself is enabled (enabled = true).
This should continue till we reach the root rid, ie. parent rid property is null.
In above example, the list of accessible rid for the user 'a.b.b.a-basa' will be:
a.b.b.a
a.b.b
a.b
and for a.a.a-basa:
a.a.a
a.a
a
can we get this list using a single query? Any sql vendor is fine.
There are several models for Hierarchical data. Most models (like the Adjacency List) require some sort of recursion for some queries. With your design that uses the Materialized Path model, what you want is possible without a recursive query.
Tested in MySQL (that has no recursive queries), at SQL-fiddle test-mysql. It can be easily converted for other DBMS, if you modify the string concatenation part:
SELECT
COUNT(*)-1 AS steps_up,
rid2.rid_key AS ancestor_rid_key
FROM
u2
JOIN
rid
ON u2.rid_key = rid.rid_key
OR u2.rid_key LIKE CONCAT(rid.rid_key, '.%')
JOIN
rid AS rid2
ON rid.rid_key = rid2.rid_key
OR rid.rid_key LIKE CONCAT(rid2.rid_key, '.%')
WHERE
u2.userid = 'basa'
AND
u2.rid_key = 'a.b.b.a'
GROUP BY
rid2.rid_key, rid2.enabled
HAVING
COUNT(*) + (rid2.enabled = 'true')
= SUM(rid.enabled = 'true') + 1 ;
It uses this view, which is not strictly needed but it shows that the user.user_id is storing data that you already have in the rid_key column.
CREATE VIEW u2 AS
SELECT
SUBSTRING_INDEX(user_id, '-', -1) AS userid
, rid_key
FROM user ;
One more note is that the above query does not use the parent_rid column at all. And that I'm sure it can be further improved.
In Oracle, you can achieve this using a hierarhical query. Search for CONNECT BY or have a look at this article.
This should get the ball rolling for you.
The answer works on SQL Server 2005 onwards
DECLARE #UsersRIDkey VARCHAR(10) = 'a.a.a'
;WITH UserCTE (userid, ridkey) AS
(
SELECT 'a-basa', 'a' UNION ALL
SELECT 'b-basa', 'b' UNION ALL
SELECT 'a.a-basa', 'a.a' UNION ALL
SELECT 'a.b-basa', 'a.b' UNION ALL
SELECT 'a.a.a-basa', 'a.a.a' UNION ALL
SELECT 'a.a.b-basa', 'a.a.b' UNION ALL
SELECT 'a.b.a-basa', 'a.b.a' UNION ALL
SELECT 'a.b.b-basa', 'a.b.b' UNION ALL
SELECT 'a.b.b.a-basa', 'a.b.b.a' UNION ALL
SELECT 'a.b.b.b-basa', 'a.b.b.b'
)
,RidCTE (ridkey, parentrid, isenabled) AS
(
SELECT 'a', null, 1 UNION ALL
SELECT 'b', null, 1 UNION ALL
SELECT 'a.a', 'a', 1 UNION ALL
SELECT 'a.b', 'a', 0 UNION ALL
SELECT 'a.a.a', 'a.a', 1 UNION ALL
SELECT 'a.b.a', 'a.b', 1 UNION ALL
SELECT 'a.b.b', 'a.b', 1 UNION ALL
SELECT 'a.b.b.a', 'a.b.b', 1
)
,RidHierarchyCTE AS
(
SELECT *
FROM RidCTE
WHERE ridkey = #UsersRIDkey
UNION ALL
SELECT R.ridkey, R.parentrid, R.isenabled
FROM RidHierarchyCTE H
JOIN RidCTE R ON R.ridkey = H.parentrid
)
SELECT ridkey
FROM RidHierarchyCTE
Oracle solution:
SQL> select u.user_id, r.rid_key, r.parent_rid, r.enabled
2 from users u
3 inner join rid r
4 on r.rid_key = u.rid_key
5 start with u.user_id = 'a.a.a-basa'
6 connect by prior r.parent_rid = r.rid_key and prior enabled = 'true'
7 /
USER_ID RID_KEY PAREN ENABL
------------ ------- ----- -----
a.a.a-basa a.a.a a.a true
a.a-basa a.a a true
a-basa a null true
SQL> select u.user_id, r.rid_key, r.parent_rid, r.enabled
2 from users u
3 inner join rid r
4 on r.rid_key = u.rid_key
5 start with u.user_id = 'a.b.b.a-basa'
6 connect by prior r.parent_rid = r.rid_key and prior enabled = 'true'
7 /
USER_ID RID_KEY PAREN ENABL
------------ ------- ----- -----
a.b.b.a-basa a.b.b.a a.b.b true
a.b.b-basa a.b.b a.b true
a.b-basa a.b a false
http://sqlfiddle.com/#!4/d529f/1