SQL concatenated names get lost in rest of result - mysql

I have a table containing zip, name and nb. If an entry has the same number (nb) as another one I wan to concatenate their names. So far it's working:
Here is SQL Fiddle with the following table and data:
CREATE TABLE `blubb` (
`zip` varchar(51) NOT NULL DEFAULT '',
`name` varchar(51) NOT NULL DEFAULT '',
`nb` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO blubb (zip, name, nb) VALUES ('1', 'aa', 1);
INSERT INTO blubb (zip, name, nb) VALUES ('2', 'bb', 2);
INSERT INTO blubb (zip, name, nb) VALUES ('3', 'cc', 3);
INSERT INTO blubb (zip, name, nb) VALUES ('4', 'dd', 2);
SELECT
zip,
name,
GROUP_CONCAT(name ORDER BY zip SEPARATOR '|') AS names
FROM blubb
GROUP BY nb
ORDER BY name ASC
Result:
+-----------------------+
| zip | name | names |
+-----------------------+
| 1 | aa | aa |
| 2 | bb | bb|dd |
| 3 | cc | cc |
+-----------------------+
But what I need is this:
+-----------------------+
| zip | name | names |
+-----------------------+
| 1 | aa | aa |
| 2 | bb | bb|dd |
| 3 | cc | cc |
| 4 | dd | dd|bb |
+-----------------------+
So if a number (nb) exists more then one time I don't want to lose it (which happend in my result). I want to select it and concatenate the names in alphabetic order. The name of the row itself should always be at first place in names followed by the other names in alphatbetic order.
Is this possible? How do I need to change my Query? I tried change the GROUP BY but nothing came closer to what I have.

You've been bitten by MySQL's notorious nonstandard extension to GROUP BY. A standard server would reject your query.
What you need is a subquery to get the list of names for each value of nb. Like so.
SELECT nb,
GROUP_CONCAT(name ORDER BY zip SEPARATOR '|') AS names
FROM blubb
GROUP BY nb
Then you join that to your detail table. Like so. (http://sqlfiddle.com/#!9/efbef6/9/0)
SELECT a.zip, a.name, b.names
FROM blubb a
JOIN (
SELECT nb,
GROUP_CONCAT(name ORDER BY zip SEPARATOR '|') AS names
FROM blubb
GROUP BY nb
) b ON a.nb = b.nb
ORDER BY a.zip

Try something like this:
SELECT q.zip, q.name,
( SELECT GROUP_CONCAT(w.name ORDER BY w.zip SEPARATOR '|')
FROM blubb AS w
WHERE w.nb = q.nb
ORDER BY w.name <> q.name, w.name ) AS names
FROM blubb AS q
ORDER BY q.name ASC
Edit
Added ordering, that the actual value would be the first. However, if there are multiple rows with the same value, all of them would go to the beginning of the list...

I would do like this:
select b.zip, b.name, g.names
from blubb b
inner join (SELECT
nb,
GROUP_CONCAT(name ORDER BY zip SEPARATOR '|') AS names
FROM blubb
GROUP BY nb) g on b.nb = g.nb
ORDER BY name ASC

Thanks to O. Jones answer it was easy to do some string manipulation to get the desired output at the end. This query is was I was looking for, maybe it's helpful to someone else:
SQL Fiddle
SELECT
a.zip,
a.name,
REPLACE(
CONCAT( a.name, '|', b.names ),
CONCAT('|', a.name),
''
) AS names
FROM blubb a
JOIN (
SELECT
nb,
GROUP_CONCAT(name ORDER BY name ASC SEPARATOR '|') AS names
FROM blubb
GROUP BY nb
) b ON a.nb = b.nb
ORDER BY a.zip
and the table with data:
CREATE TABLE `blubb` (
`zip` varchar(51) NOT NULL DEFAULT '',
`name` varchar(51) NOT NULL DEFAULT '',
`nb` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO blubb (zip, name, nb) VALUES ('1', 'aa', 1);
INSERT INTO blubb (zip, name, nb) VALUES ('2', 'bb', 2);
INSERT INTO blubb (zip, name, nb) VALUES ('3', 'cc', 3);
INSERT INTO blubb (zip, name, nb) VALUES ('4', 'dd', 2);
INSERT INTO blubb (zip, name, nb) VALUES ('5', 'ab', 2);
INSERT INTO blubb (zip, name, nb) VALUES ('5', 'cb', 2);
INSERT INTO blubb (zip, name, nb) VALUES ('6', 'ca', 2);
here is SQL Fiddle with alphabetical order on the resulting names

Related

Group and subquery issue

This is my sample data
CREATE TABLE customer1
(
rating int(9),
genre varchar(100),
title varchar(100)
);
INSERT INTO customer1 (rating, genre, title)
VALUES
(2, 'A', 'abc'),
(4, 'A', 'abc1'),
(2, 'B', 'abc2'),
(3, 'B', 'abc3'),
(2, 'C', 'abc4'),
(5, 'C', 'abc5');
I need to find the title with max rating in each genre.
Thanks for the help.
One option uses a subquery for filtering:
select c.*
from customer1
where c.rating = (select max(c1.rating) from customer1 c1 where c1.genre = c.genre)
This would take advantage of an index on (genre, rating).
In MySQL 8.0, you can also use window functions:
select *
from (
select c.*,
rank() over(partition by genre order by rating desc) rn
from customer1 c
) c
where rn = 1

mysql, query how to goup same value and count that value

i have data table like that :
month IKU value
2 1b 1
2 1b 1
1 2a 1
1 1a 1
the results I want like that :
month IKU value
2 1b 2
1 1a,2a 1,1
is that possible?
You need to use GROUP BY twice along with GROUP_CONCAT function like below:
SELECT
tmp.month,
GROUP_CONCAT(tmp.IKU) AS IKU,
GROUP_CONCAT(tmp.summedValue) AS value
FROM
(
SELECT
month,
IKU,
SUM(value) AS summedValue
FROM t
GROUP BY month,IKU
) AS tmp
GROUP BY tmp.month
ORDER BY tmp.month DESC
GROUP_CONCAT() function
MySQL GROUP_CONCAT() function returns a string with concatenated non-NULL value from a group.
Returns NULL when there are no non-NULL values.
Test:
-- ----------------------------
-- Table structure for `t`
-- ----------------------------
DROP TABLE IF EXISTS `t`;
CREATE TABLE `t` (
`month` int(11) DEFAULT NULL,
`IKU` varchar(10) DEFAULT NULL,
`value` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- ----------------------------
-- Records of t
-- ----------------------------
INSERT INTO `t` VALUES ('2', '1b', '1');
INSERT INTO `t` VALUES ('2', '1b', '1');
INSERT INTO `t` VALUES ('1', '2a', '1');
INSERT INTO `t` VALUES ('1', '1a', '1');
Try this. Didn't get chance to test it as MySQL fiddle isn't working, but the idea is to first sum(value) for each combination of month,iku. Then use group_concat to concatenate the resultset.
select month
,group_concat(IKU separator ',')
,group_concat(value separator ',')
from
(select month,IKU,sum(value) as value
from Table1 group by month,IKU
) t
group by IKU

Converting column into row using "|" as separators

Please take a look at this Fiddle Example
I want to convert each field into a row from this table:
CREATE TABLE product
(`ID` int, `name` varchar(1), `category` varchar(11), `price` int,`year`int)
;
INSERT INTO product
(`ID`, `name`, `category`, `price`,`year`)
VALUES
(1, 'A', 'Outdoor', 25,2010),
(2, 'A', 'Doll', 34,2009),
(3, 'C', 'Doll', 25,2008),
(4, 'D', 'Outdoor', 20,2010),
(5, 'E', 'Brainteaser', 22,2010),
(6, 'E', 'Brainteaser', 22,2009),
(7, 'G', 'Brainteaser', 30,2009),
(8, 'G', 'Brainteaser', 30,2009)
;
Here's the output I'm trying to get:
field value
name A,C,D,E,G
category Brainteaser,Doll,Outdoor
price 20,22,25,30,34
year 2008,2009,2010
I read a thread about pivoting table with UNION and MAX but I was lost at using MAX with GROUP_CONCAT
SELECT
MAX(CASE WHEN ... GROUP_CONCAT(DISTINCT (value) SEPARATOR '|')) as value
from(
select id,name value, 'name' field
from product
union all
select id,category value, 'category' field
from product
union all
select id,price value, 'price' field
from product
union all
select id,year value, 'year' field
from product
)
GROUP BY field
order by value
Can anyone show me how to get that output?
This will give you expected output:
SELECT 'name' AS `field`. GROUP_CONCAT(DISTINCT `name` ORDER BY `name`) AS `value`
FROM product
UNION ALL
SELECT 'category' AS `field`. GROUP_CONCAT(DISTINCT `category` ORDER BY `category`) AS `value`
FROM product
UNION ALL
SELECT 'price' AS `field`. GROUP_CONCAT(DISTINCT `price` ORDER BY `price`) AS `value`
FROM product
UNION ALL
SELECT 'year' AS `field`. GROUP_CONCAT(DISTINCT `year` ORDER BY `year`) AS `value`
FROM product
Added ORDER BY because looks like you need sorted output
something like this?? Using union all for better proformance and incase there are any dupilcates.
SELECT 'name' field, group_concat(DISTINCT name ORDER BY name SEPARATOR '|') value FROM product
UNION ALL
SELECT 'category' field, group_concat(DISTINCT category ORDER BY category SEPARATOR '|') value FROM product
UNION ALL
SELECT 'price' field, group_concat(DISTINCT price ORDER BY price SEPARATOR '|') value FROM product
UNION ALL
SELECT 'year' field, group_concat(DISTINCT year ORDER BY year SEPARATOR '|') value FROM product;
EDIT:
If you would like to do this with just one query you can achieve it this way.
SELECT
#uName := group_concat(DISTINCT name ORDER BY name SEPARATOR '|'),
#uCat := group_concat(DISTINCT category ORDER BY category SEPARATOR '|') uCat,
#uPrice := group_concat(DISTINCT price ORDER BY price SEPARATOR '|') uPrice,
#uYear := group_concat(DISTINCT year ORDER BY year SEPARATOR '|') uYear
FROM product;
SELECT 'name' field, #uName value
UNION ALL
SELECT 'category' field, #uCat value
UNION ALL
SELECT 'price' field, #uPrice value
UNION ALL
SELECT 'year' field, #uYear value;
NOTE: you can do ORDER BY inside the GROUP_CONCAT
There is a way to complete your request without having to query your table with many queries. In fact, method below is a way to turn any table to pivot. It will use mysql prepared statements for prepare SQL and later execute it:
SELECT
GROUP_CONCAT(f SEPARATOR ' UNION ALL ')
FROM
(SELECT
CONCAT(
'SELECT "',
column_name,
'" AS `field`, GROUP_CONCAT(DISTINCT ',
column_name,
') AS `value` FROM `',
#table_name,
'`'
) AS f
FROM
((SELECT
column_name
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
table_name=#table_name &&
table_schema=#schema_name
) AS fields
CROSS JOIN
(SELECT
#table_name := 'product',
#schema_name:= 'test'
) AS init)
) AS sqldata
The sql above will result in a string, which can be used to assign a variable, like
mysql> SET #sql:=(SELECT GROUP_CONCAT(f SEPARATOR ' UNION ALL ') FROM (SELECT CONCAT('SELECT "', column_name,'" AS `field`, GROUP_CONCAT(DISTINCT ', column_name,') AS `value` FROM `', #table_name, '`') AS f FROM ((SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name=#table_name && table_schema=#schema_name) AS fields CROSS JOIN (SELECT #table_name:='product', #schema_name:='test') AS init)) AS sqldata);
Query OK, 0 rows affected (0.03 sec)
next, prepare:
mysql> PREPARE stmt FROM #sql;
Query OK, 0 rows affected (0.00 sec)
Statement prepared
finally, execute it:
mysql> EXECUTE stmt;
+----------+--------------------------+
| field | value |
+----------+--------------------------+
| ID | 1,2,3,4,5,6,7,8 |
| name | A,C,D,E,G |
| category | Outdoor,Doll,Brainteaser |
| price | 25,34,20,22,30 |
| year | 2010,2009,2008 |
+----------+--------------------------+
5 rows in set (0.00 sec)
The benefit is that you are independent from table structure, field names, e tc. It's a general way - so you may use it to create such pivots for any table.
Few words about variables #table_name and #schema_name. They specify which table and in which schema would be pivoted. In sample above, I used CROSS JOIN to set them in-place, but you may set them separately, to maintain different tables for pivots.

join user id from tblB to user from tblA and get username

how to join userid to user and get the username ?
I really appreciate any help.Thanks in Advance.
http://sqlfiddle.com/#!2/ac600/1
CREATE TABLE if not exists tblA
(
id int(11) NOT NULL auto_increment ,
user varchar(255),
category int(255),
PRIMARY KEY (id)
);
CREATE TABLE if not exists tblB
(
id int(11) NOT NULL auto_increment ,
username varchar(255),
userid int(255),
PRIMARY KEY (id)
);
INSERT INTO tblA (user, category ) VALUES
('1', '1'),
('1', '2'),
('1', '3'),
('1', '1'),
('2', '1'),
('2', '1'),
('2', '1'),
('2', '1'),
('3', '1'),
('2', '1'),
('4', '1'),
('4', '1'),
('2', '1');
INSERT INTO tblB (userid, username ) VALUES
('1', 'A'),
('2', 'B'),
('3', 'C'),
('4', 'D'),
('5', 'E');
query:
SELECT
groups.*,
#rank:=#rank+1 AS rank
FROM
(select
user,
category,
count(*) as num
from
tblA
where
category=1
group by
user,
category
order by
num desc,
user) AS groups
CROSS JOIN (SELECT #rank:=0) AS init
the table looks like :
username category num Ascending rank
B 1 6 2
A 1 2 1
D 1 2 4
C 1 1 3
Use JOIN, for example:
SELECT
tblB.username,
groups.*,
#rank:=#rank+1 AS rank
FROM
(select
user,
category,
count(*) as num
from
tblA
where
category=1
group by
user,
category
order by
num desc,
user) AS groups
-- left join: in case if data integrity fails:
left join
tblB ON groups.user=tblB.userid
CROSS JOIN (SELECT #rank:=0) AS init
-check your modified demo.
You just need to do left join
SELECT
groups.*,
#rank:=#rank+1 AS rank
FROM
(select
user,
category,
count(*) as num,
tblB.username
from
tblA
left join tblB on tblA.id = tblB.userid
where
category=1
group by
user,
category
order by
num desc,
user) AS groups
CROSS JOIN (SELECT #rank:=0) AS init
See Demo
You don't need a subquery to do what you want. You can simply join in the name:
select username, category,
count(*) as num,
#rank:=#rank+1 AS rank
from tblA join
tblB
on tblA.user = tblB.userId CROSS JOIN
(SELECT #rank:=0) AS cont
where category = 1
group by username, category
order by num desc, username;
TblB has an odd format. Normally, the auto-incrementing id would be the "userid" for the table.
Also, because you are selecting only one category, strictly speaking it is unnecessary to put category in the group by statement.
EDIT:
You cannot create a view with this method of doing the rank because it uses variables. It isn't easy to generate a rank on aggregated data in a view-compatible way in MySQL.

rank users based on count num

how to rank users based on column num which is count of number of times user exists in table.Also if 2 users have same count it should give the user on top rank lesser than below eg: user 1 and 2 has count 3 then user 1 is rank 1 and user 2 is rank 3 and if there is new user 3 with count 1 then user 3 is rank 3 .
How do I do that? I really appreciate any help.Thank in Advance.
http://sqlfiddle.com/#!2/a9188/2
CREATE TABLE if not exists tblA
(
id int(11) NOT NULL auto_increment ,
user varchar(255),
category int(255),
PRIMARY KEY (id)
);
INSERT INTO tblA (user, category ) VALUES
('1', '1'),
('1', '2'),
('1', '3'),
('1', '1'),
('2', '1'),
('2', '1'),
('2', '1'),
('2', '1'),
('3', '1'),
('2', '1');
Response like: Search for category where its '1'
user category count rank
1 1 2 1
2 1 2 2
query used:
SELECT USER,
category,
count(*) AS num
FROM tblA
WHERE category=1
GROUP BY USER,
category
ORDER BY num DESC;
For example, using subquery:
SELECT
groups.*,
#rank:=#rank+1 AS rank
FROM
(select
user,
category,
count(*) as num
from
tblA
where
category=1
group by
user,
category
order by
num desc,
user) AS groups
CROSS JOIN (SELECT #rank:=0) AS init
-check your modified demo.