MySql : How do I have Horizontal Union of results? - mysql

I am looking to generate the results into the horizontal union. I could able to combine together and generate the results. However, I need to show the results in the expected results format.
SQL
(SELECT
kpa_id AS 'KPA', SUM(weightage) AS 'Total'
FROM
pmm_question_details
WHERE
weightage NOT LIKE '%-%'
GROUP BY kpa_id) UNION (SELECT
kpa_id AS 'KPA', SUM(weightage_value) AS 'Acheived'
FROM
pmm_answer_details
WHERE
application_id = 2
AND archive_value = 'No'
GROUP BY kpa_id)
Actual Results
1 14
2 37
3 19
4 40
5 51
6 24
1 12
2 19
3 0
6 2
Expected Results
1 14 1 12
2 37 2 19
3 19 3 0
4 40 6 2
5 51
6 24

If we can assume pmm_question_Details will always have as many or more records than pmm_answer_details... then two subqueries and a left join should do the trick with a join on a uservariable rownum (RN)
SELECT A.KPA, A.Total, B.KPA, B.Acheived
FROM (SELECT kpa_id AS 'KPA'
, SUM(weightage) AS 'Total'
, #RN1 := #RN1 + 1 as RN
FROM pmm_question_details
CROSS JOIN (SELECT #RN1 :=0) r1
WHERE weightage NOT LIKE '%-%'
ORDER BY KPA
GROUP BY kpa_id) A
LEFT JOIN (SELECT kpa_id AS 'KPA'
, SUM(weightage_value) AS 'Acheived'
, #RN1 := #RN2 + 1 as RN
FROM pmm_answer_details
CROSS JOIN (SELECT #RN2 :=0) r2
WHERE application_id = 2
AND archive_value = 'No'
ORDER BY KPA
GROUP BY kpa_id) B
on A.RN = B.RN
ORDER BY A.KPA
Though I must admit I don't see why a rownumber is needed if you could just left join on the KPA_ID in the first place...
if this could be the Expected results... (and again assuming pmm_question has all the IDs which could be in pmm_answer... )
Expected Results
1 14 1 12
2 37 2 19
3 19 3 0
4 40
5 51
6 24 6 2
Then the query would just be...
SELECT A.KPA, A.Total, B.KPA, B.Acheived
FROM (SELECT kpa_id AS 'KPA', SUM(weightage) AS 'Total'
FROM pmm_question_details
WHERE weightage NOT LIKE '%-%'
GROUP BY kpa_id) A
LEFT JOIN (SELECT kpa_id AS 'KPA', SUM(weightage_value) AS 'Acheived'
FROM pmm_answer_details
WHERE application_id = 2
AND archive_value = 'No'
GROUP BY kpa_id) B
on A.KPA = B.KPA

Related

Mysql Select X rows after specific match

I'm trying to write a select statement in MySQL to get 5 rows after I get my match then sum how many times those numbers were repeated.
Example:
Raw Table
id
number
1
1
2
0
3
9
4
14
5
11
6
0
7
3
8
4
9
10
10
9
11
0
12
5
13
3
14
11
15
0
I need to find every row with the number 0, then after that select and show 5 rows after and counting the appearance of the numbers.
How can I select the numbers and get the count as a result?
The result of the first select should be like this:
id
number
3
9
4
14
5
11
6
0
7
3
7
3
8
4
9
10
10
9
11
0
12
5
13
3
14
11
15
0
The result of the count for every number in the last query.
Number
Count
9
2
14
1
11
2
0
3
3
3
4
1
10
1
5
1
This is a demo to get expected results:
Select numbers:
SELECT id, number
FROM (
SELECT b.id, b.number, ROW_NUMBER() OVER(PARTITION BY a.id ORDER BY b.id ASC) r
FROM numbers a
JOIN numbers b ON a.id < b.id
WHERE a.number = 0
) t WHERE t.r <= 5
ORDER BY id
Count numbers:
WITH n AS (
SELECT id, number
FROM (
SELECT b.id, b.number, ROW_NUMBER() OVER(PARTITION BY a.id ORDER BY b.id ASC) r
FROM numbers a
JOIN numbers b ON a.id < b.id
WHERE a.number = 0
) t WHERE t.r <= 5
)
SELECT number, COUNT(*) counts
FROM n
GROUP BY number
Sample data:
CREATE TABLE numbers (
id INT PRIMARY KEY auto_increment,
number INT NOT NULL
);
INSERT INTO numbers ( number ) VALUES (1),(0),(9),(14),(11),(0),(3),(4),(10),(9),(0),(5),(3),(11),(0);
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=1fe71080cfb27680eb2a37b721e5de2d
Update for MySQL v5.7
SELECT n.*
FROM numbers n
JOIN (
SELECT a.id, SUBSTRING_INDEX(GROUP_CONCAT(b.id ORDER BY b.id SEPARATOR ','), ',', 5) selections
FROM numbers a
JOIN numbers b ON a.id < b.id
WHERE a.number = 0
GROUP BY a.id
) t ON FIND_IN_SET(n.id, t.selections)
ORDER BY n.id
SELECT n.number, COUNT(*) counts
FROM numbers n
JOIN (
SELECT a.id, SUBSTRING_INDEX(GROUP_CONCAT(b.id ORDER BY b.id SEPARATOR ','), ',', 5) selections
FROM numbers a
JOIN numbers b ON a.id < b.id
WHERE a.number = 0
GROUP BY a.id
) t ON FIND_IN_SET(n.id, t.selections)
GROUP BY n.number
ORDER BY n.number
https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=3be09acab5cd696ec4b01585eb5c32ed

Select difference based on record having minimum and maximum date in MySql

Below is my table let's call account
**ID accountID score tracking_date
1 1 3 2014-09-25 00:01:05
2 2 4 2014-09-26 01:05:18
3 1 6 2014-09-27 09:23:05
4 2 9 2014-09-28 20:01:05
5 1 1 2014-09-28 23:21:34
6 3 7 2014-09-21 00:01:00
7 2 1 2014-09-22 01:45:24
8 2 9 2014-09-27 14:01:43
9 3 1 2014-09-24 22:01:27
I want to select record with max date and also the difference of score with the records having tracking_date as minimum for that accountId. So I want output like below
ID accountID score_with_maxdate diff_score_with_mindate max_tracking_date
1 1 1 -2 2014-09-28 23:21:34
2 2 9 8 2014-09-28 20:01:05
3 3 1 -6 2014-09-24 22:01:27
Any help?
Here is one option. We can self-join a subquery which finds both the min and max tracking dates, for each account, twice to your original table. This will bring in all metadata for those max tracking date records, including the scores.
SELECT
t1.accountID,
t2.score AS score_with_maxdate,
t2.score - t3.score AS diff_score_with_mindate,
t1.max_tracking_date
FROM
(
SELECT
accountID,
MAX(tracking_date) AS max_tracking_date,
MIN(tracking_date) AS min_tracking_date
FROM yourTable
GROUP BY accountID
) t1
INNER JOIN yourTable t2
ON t1.accountId = t2.accountID AND t2.tracking_date = t1.max_tracking_date
INNER JOIN yourTable t3
ON t1.accountId = t3.accountID AND t3.tracking_date = t1.min_tracking_date
ORDER BY
t1.accountID;
Demo
This is a somewhat tricky question. I think conditional aggregation is a convenient way to solve the problem:
select min(t.id) as id, t.accountId,
max(case when t.tracking_date = t2.max_td then t.score end) as score_with_maxdate,
max(case when t.tracking_date = t2.max_td then t.score
when t.tracking_date = t2.min_td then - t.score
end) as diff_score_with_mindate,
max(t.tracking_date) as max_tracking_date
from t join
(select t2.accountId, min(t2.tracking_date) as min_td, max(t2.tracking_date) as max_td
from t t2
group by t2.accountId
) t2
on t.accountId = t2.accountId
group by t.accountId;
Another hackish way of getting same results by using aggregate and string fucntion
select t.accountID,
t.score_with_maxdate,
t.score_with_maxdate - t.score_with_mindate score_with_maxdate,
t.max_tracking_date
from(
select accountID,
substring_index(group_concat(score order by tracking_date desc),',', 1) + 0 score_with_maxdate,
substring_index(group_concat(score order by tracking_date asc),',', 1) + 0 score_with_mindate,
max(tracking_date) max_tracking_date
from demo
group by accountID
) t
Demo
But i would suggest you to go with other solutions mentioned by Tim & Gordon

MySQL combining multiple rows into one row

I've two tables as below:
1. tbl_student:
id name
1 raj
2 raja
3 raju
4 rajan
tbl_attendance
id student_id month attended_days
1 1 1 6
2 1 2 16
3 8 1 8
4 7 2 14
5 8 2 13
6 7 1 11
I need to join these two tables combining multiple rows for each month for each student from tbl_attendance into a single row to obtain a result like this:
id name month attended_days month attended_days
1 raj 1 6 2 16
7 raja 1 11 2 14
8 rajan 1 8 2 13
Thanks in advance for any help.
Instead of displaying month value in each of the records,
you can use them as column headers and attendance as their value.
Use pivot type solution to achieve required solution.
Example:
select s.id as student_id
, s.name as student_name
, max( case when a.month = 1 then a.attended_days else null end ) as month_1
, max( case when a.month = 2 then a.attended_days else null end ) as month_2
, max( case when a.month = 3 then a.attended_days else null end ) as month_3
-- ...
, max( case when a.month = 12 then a.attended_days else null end ) as month_12
from table_student s
left join table_attendance a on s.id = a.student_id
group by s.id, s.name
Your question is not very complete, but i think you want something like this:
select s.*,
coalesce(a1.month, a2.month, a3.month) as month,
coalesce(a1.attended_days , a2.attended_days , a3.attended_days ) as attended_days
from table_student s
left join table_attendance a1 on s.id = a1.student_id and a1.month = 1
left join table_attendance a2 on s.id = a2.student_id and a2.month = 2
left join table_attendance a3 on s.id = a3.student_id and a3.month = 3
The previous code is used if you want to show all months in one column. For multiple columns, you can use this example:
select s.*,
a1.month as month_1,
a2.month as month_2,
a3.month as month_3,
a1.attended_days as attended_days_1,
a2.attended_days as attended_days_2,
a3.attended_days as attended_days_3
from table_student s
left join table_attendance a1 on s.id = a1.student_id and a1.month = 1
left join table_attendance a2 on s.id = a2.student_id and a2.month = 2
left join table_attendance a3 on s.id = a3.student_id and a3.month = 3
Do this for all the 12 months. I used 3 as example.

MySQL :: group by between price AND two dates

I have a table like following columns:
id date price pid
---------------------------
1 2015-02-01 34 2
2 2015-02-02 34 2
3 2015-02-03 34 2
4 2015-02-04 78 2
5 2015-02-05 78 2
6 2015-02-06 78 2
7 2015-02-07 52 2
8 2015-02-08 52 2
9 2015-02-09 52 2
10 2015-02-10 34 2
11 2015-02-11 34 2
12 2015-02-12 34 2
Now I want following result:
date_from date_to price pid
-------------------------------------
2015-02-01 2015-02-03 34 2
2015-02-04 2015-02-06 78 2
2015-02-07 2015-02-09 52 2
2015-02-10 2015-02-12 34 2
IMPORTANT:
I don't want to group the price "34" in this case.
One solution i can think of using user defined variables also i assume the id part is set to auto_increment
select min(t1.date) date_from,
max(t1.date) date_to,
t1.price,
t1.pid
from (
select t.*,
#r:= case when #g = price then #r else #r + 1 end r,
#g:= price g
from test t
cross join (select #g:=null,#r:=0) t
order by id
) t1
group by t1.r
DEMO
Borrowing M Khalid Junaid's fiddle...
SELECT a.date date_from
, MIN(c.date) date_to
, price
, pid
FROM test a
LEFT
JOIN test b
ON b.pid = a.pid
AND b.price = a.price
AND b.id = a.id - 1
LEFT
JOIN test c
ON c.pid = a.pid
AND c.price = a.price
AND c.id >= a.id
LEFT
JOIN test d
ON d.pid = a.pid
AND d.price = a.price
AND d.id = c.id + 1
WHERE b.id IS NULL
AND c.id IS NOT NULL
AND d.id IS NULL
GROUP
BY a.id, a.price, a.pid;
http://sqlfiddle.com/#!2/478f9/6
Try below query, it will group by three records on basis of date and price
SELECT min(created) AS date_from, max(created) AS date_to, price, pid
FROM t1
GROUP BY price, floor(DATEDIFF("2015-02-01", created) / 3);
Try this in MySQL. Here app_date is Date in your question:
set #rownum=1;
select min(app_date),max(app_date),price,pid from
(select t.id1 id,t.app_date app_date,t.price1 price,t.pid pid,
case when (t.price1=t.price2 or t.price2 is null) then #rownum else #rownum:=#rownum+1 end temp_num from
(select a.id id1,b.id id2,a.app_date app_date,a.price price1,b.price price2,a.pid pid from
test a left outer join test b on a.id=b.id+1) t
order by id1) temp
group by price,pid,temp_num
order by min(app_date);

How to make a fake column with an autoincrement number in a "group by" query

I have data in a table like this:
fgid qty ntid
1 100 10
2 90 10
6 200 11
1 80 11
1 120 12
6 100 12
6 30 13
And i make query :
SELECT fgid, SUM(qty) AS total_qty, COUNT(ntid) AS nt_count FROM sofg
GROUP BY fgid
AND the result is :
fgid total_qty nt_count
1 300 3
2 90 1
6 330 3
Then i want to make the result like this :
no fgid total_qty nt_count
1 1 300 3
2 2 90 1
3 6 330 3
How to do that with a query? where 'no' is (like) autoincrement number.
Try this query.
SELECT
#rownum := #rownum + 1 rownum,
t.*
FROM (SELECT #rownum:=0) r,
(
SELECT fgid, SUM(qty) AS total_qty, COUNT(ntid) AS nt_count FROM sofg GROUP BY fgid
) t;
Basically the same as Dhinakaran's answer, but there's no need to put the whole main query into a subquery. There's no difference to his answer appart from maybe being more pleasing to the eye, but please accept Dhinakaran's answer, as he was faster.
SELECT
#rownum:=#rownum + 1 as rownumber,
fgid,
SUM(qty) AS total_qty,
COUNT(ntid) AS nt_count
FROM sofg
, (select #rownum:=0) v
GROUP BY fgid