Retrieve data in a single row using group keyword in sql - mysql

Let's say I have a table tbl_marks with columns and data as below.
name id section1 section2 section3 section4 year
cherry 1 100 101 102 103 2016
cherry 1 200 201 202 203 2015
cherry 1 300 301 302 303 2014
Expected Output Format :
cherry 1 100101102103 200201202203 300301302303
I would like to have scores of all sections of one year to be concatenated and then followed by scores of another year separated by space.
So I need 5 columns in single row. (name, id, scores of year1, scores of year2, scores of year3)
Please let me know how should i update the query below. Thank you.
Query : select name, id, ??? from tbl_marks group by id;

Use GROUP_CONCAT:
SELECT name,
id,
GROUP_CONCAT(CONCAT(section1, section2, section3, section4)
ORDER BY section1 SEPARATOR ' ')
FROM yourTable
GROUP BY name, id
This answer assumes that you want three columns in your result set, the three columns being the name, id, and a CSV list of marks for that name/id group (123,456,789 in this case).
SQLFiddle (courtesy of #Luke)

The process you are looking for is called 'pivoting' and is not dynamically possible in basic SQL as it is a set-based language.
Just retrieve the rows in your business logic code and turn it into columns there.

All you need to use is group_concat() and concat() functions of MySQL, e.g.
select name, group_concat(concat(value1, value2))
from table
group by name
Here is the sample SQL Fiddle.

One way to solve this is self joins.
SELECT a.id,a.name,CONCAT(a.section1, a.section2, a.section3, a.section4) as val2015
,CONCAT(b.section1, b.section2, b.section3, b.section4) as val2016
,CONCAT(c.section1, c.section2, c.section3, c.section4) as val2017
FROM test a
LEFT JOIN test b
ON b.id=a.id
LEFT JOIN test c
ON c.id=a.id
WHERE a.year=2015 AND b.year=2016 AND c.year=2017
SQL Fiddle
Edit: Changed the query according to comments.

Related

Gathering data from three separate tables, sql

I have three separate tables that represent student attendance for three weeks, respectively. I want to be able to generate four columns that break down the attendance by week for each of the students. If a student was present multiple times a week, the number of times present should be added. Also, if a student was present in one week and not the next, it would get 1 for the month present (assuming it was only present once) and and 0 for the one absent. I have tried to multiple variations of count() and joins but to no avail. Any help would be greatly appreciated. The following is a truncated fiddle:
http://www.sqlfiddle.com/#!9/b847a
Here is a sample of what I am trying to achive:
Name | CurrWeek | LastWeek | TwoWkAgo
Paula | 0 | 2 | 3
Rather than three tables you should have only one with a column for the week. So naturally one solution for your request is to build it on-the-fly with UNION ALL:
select
name,
sum(week = 'currentWeek') as currentWeek,
sum(week = 'lastWeek') as lastWeek,
sum(week = 'thirdWeek') as thirdWeek
from
(
select 'currentWeek' as week, name from currentWeek
union all
select 'lastWeek' as week, name from lastWeek
union all
select 'thirdWeek' as week, name from thirdWeek
) all_weeks
group by name
order by name;
(If you want to join the three tables instead, you'd need full outer joins, which MySQL does not support, if I remember correctly. Anyway, my advice is to change the data model.)
You can try this query:
select currweek.name, currweek.att, lastweek.att, twoWkAgo.att from
(select name, count(attendance) as att from currentWeekTable group by name) currweek,
(select name, count(attendance) as att from lastWeekTable group by name) lastweek,
(select name, count(attendance) as att from twoWeekTable group by name) twoWkAgo
where twoWkAgo.name=currWeek.name and twoWkAgo.name=lastweek.name;
Assuming your 3 attendance tables contain name as common field.

How to select rows in which data in a certain column is never repeated-SQL

I have a table with just 4 rows.
FIRST LAST AGE
luke david 42
jester prince 32
luke mike 16
sean paul 22
I want to select only those rows in which the first name is never repeated. So the result I need is
FIRST LAST AGE
jester prince 32
sean paul 22
The sql query should ignore all the rows in which the first name is being repeated. The simple distinct query doesn't work here because it just removes multiple entries. I want a query which can remove ALL the rows of the repeating first name.
TRY
SELECT * FROM `tbl_name` GROUP BY(`name`) HAVING COUNT(`name`) = 1
You can use group by if distinct does not work
SELECT * FROM Table GROUP BY FIRST
You may use a NOT IN clause, like this:
SELECT * FROM table1
WHERE first NOT IN (
SELECT first FROM table1
GROUP BY first
HAVING COUNT(*) > 1
)
Inner query finds all first names which are repeated and then using the NOT IN clause those duplicate first names are removed from the final result.
select FIRST, LAST, AGE
from MY_TABLE
group by FIRST
HAVING count(FIRST) = 1

mysql group concat improper ordering

I have a query which returns records in following order from db:
app_id app_name transaction_id mobile_no logtime_stamp navigation_type entered_code display_text User_Response
111 Unicef 1133 919552516853 10/5/2012 12:52:37 PM 0 Maharashtra Student Attendance 2
111 Unicef 1133 919552516853 10/5/2012 12:52:37 PM 0 Pune Student Attendance 2
111 Unicef 1133 919552516853 10/5/2012 12:52:37 PM 0 Baramati Student Attendance 2
111 Unicef 1133 919552516853 10/5/2012 12:52:37 PM 0 Ravi School Student Attendance 2
The query that returns the above records is an inner query.
I am doing a group_concat on these records to get a single row.
I am getting the following records:
app_name transaction_id mobile_no entered_code display_text User_Response
Unicef 1133 919552516853 Baramati,Ravi School,Maharashtra,Pune Student Attendance 2
Now I dont understand on what basis is the group_concat function ordering the string - Baramati,Ravi School,Maharashtra,Pune!?
I want the ordering to be exactly the same as I am getting in the first set:
Maharashtra,Pune,Baramati,Ravi School
As far as I know, a string in group_concat is sorted alphabetically by default. But the above result defies that.
Also I tried a sample query where in I am reading records from a table and group_concating that without assigning any specific order. The result had string sorted as per the insertion order. That is, the record that was inserted first came up first in the concat string and the last record as the last in the string.
So can I arrange/order my result set in the group_concat funcation based on the read order of the inner query?
Just looked at the docs: http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_group-concat
Found this line:
To sort values in the result, use the ORDER BY clause.
as well as this, as an example:
mysql> SELECT student_name,
-> GROUP_CONCAT(DISTINCT test_score
-> ORDER BY test_score DESC SEPARATOR ' ')
-> FROM student
-> GROUP BY student_name;
group_concat(entered_code order by entered_code)
Without seeing the full query and table data prior to the results you gave, it is difficult to tell of you can add an ORDER BY to you GROUP_CONCAT() however, it appears to be working fine in this demo:
select app_name,
transaction_id,
mobile_no,
group_concat(entered_code),
display_text,
User_Response
from yourtable
See SQL Fiddle with Demo.
As for documentation you have to be able to order group concat result based on some field or expression. http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_group-concat
You should not rely on an "insertion order" at all since there is no such feature in MySQL. In most case you see data in this order, but it is not being ordered actually and later you go less relevant this order will become.

find out count of comma based value in MySql

I have two tables.
Table Emp
id name
1 Ajay
2 Amol
3 Sanjay
4 Vijay
Table Sports
Sport_name Played by
Cricket ^2^,^3^,^4^
Football ^1^,^3^
Vollyball ^4^,^1^
Now I want to write a query which will give me output like
name No_of_sports_played
Ajay 2
Amol 1
Sanjay 2
Vijay 2
So what will be Mysql query for this?
I agree with the above answers/comments that you are not using a database for what a database is for, but here is how you could calculate your table from your current structure in case you have no control over that:
SELECT Emp.name, IF(Played_by IS NULL,0,COUNT(*)) as Num_Sports
FROM Emp
LEFT JOIN Sports
ON Sports.Played_by RLIKE CONCAT('[[:<:]]',Emp.id,'[[:>:]]')
GROUP BY Emp.name;
See it in action here.
UPDATE: added the IF(Played_by IS NULL,0,COUNT(*)) instead of COUNT(*). This means that if an employee doesn't play anything they'll have a 0 as their Num_Sports. See it here (I also added in those ^ characters and it still works.
What it does is joins the Emp table to the Sports table if it can find the Emp.id in the corresponding Played_by column.
For example, if we wanted to see what sports Ajay played (id=1), we could do:
SELECT *
FROM Emp, Sports
WHERE Sports.Played_by LIKE '%1%'
AND Emp.id=1;
The query I gave as my solution is basically the query above, with a GROUP BY Emp.name to perform it for each employee.
The one modification is the use of RLIKE instead of LIKE.
I use RLIKE '[[:<:]]employeeid[[:>:]]' instead of LIKE '%employeeid%. The [[:<:]] symbols just mean "make sure the employeeid you match is a whole word".
This prevents (e.g.) Emp.id 1 matching the 1 in the Played_by of 3,4,11,2.
You do not want to store your relationships in a column like that. Create this table:
CREATE TABLE player_sports (player_id INTEGER NOT NULL, sport_id INTEGER NOT NULL, PRIMARY KEY(player_id, sport_id));
This assumes you have an id column in your sports table. So now a player will have one record in player_sports for each sport they play.
Your final query will be:
SELECT p.name, COUNT(ps.player_id)
FROM players p, player_sports ps
WHERE ps.player_id = p.id
GROUP BY p.name;

SQL GROUP BY - Multiple results in one column?

I am trying to perform a SELECT query using a GROUP BY clause, however I also need to access data from multiple rows and somehow concatenate it into a single column.
Here's what I have so far:
SELECT
COUNT(v.id) AS quantity,
vt.name AS name,
vt.cost AS cost,
vt.postage_cost AS postage_cost
FROM vouchers v
INNER JOIN voucher_types vt
ON v.type_id = vt.id
WHERE
v.order_id = 1 AND
v.sold = 1
GROUP BY vt.id
Which gives me the first four columns I need in the following format.
quantity | name | cost | postage_cost
2 X 5 1
2 Y 6 1
However, I would also like a fifth column to be displayed, showing all of the codes associated with each line of the order like this:
code
ABCD, EFGH
IJKL, MNOP
Where the comma separated values are pulled from the voucher table.
Is this possible?
Any advice would be appreciated.
Thanks
This is what GROUP_CONCAT does.
Assuming the column is called code you would just add ,GROUP_CONCAT(v.code) As Codes to your select list.