is it possible to have numbering in GROUP_CONCAT
like
If, from GROUP_CONCAT(empnam SEPARATOR ', ')
I get a set,
< JohnM, DannyP, TiffnyK, KarlM >
I need to have
< 1.JohnM, 2.DannyP, 3.TiffnyK, 4.KarlM >
I tried following, but didnt get desired results.
SET #x:=0;
SELECT
GROUP_CONCAT(#x:=#x+1,' ', s.empnam SEPARATOR ', ') AS emps, #x:=0
< tables >
< filters >
is it possible at Query-Level, or I have to do it at Application Side ?
Years later, we should abandon mutating variables inside a select statement, as since MySQL 8 we can use the standard way, with window functions:
with base as (
select dep,
empnam,
count(*) over (partition by dep order by empnam) num
from t)
select dep,
group_concat(concat(num, '.', empnam) separator ', ') emps
from base
group by dep
See db-fiddle
Original answer (2016)
You can do this on the application side, but in MySQL 5.7 it is possible. In the following query, I assume you group the names by something, for example their department (I called it dep). This in order to illustrate that the counter starts from 1 for every new group.
select dep,
group_concat(
concat(#i := if (#grp = dep, #i + 1, if(#grp := dep,1,1)), '.', empnam)
separator ', ') emps
from t,
(select #i := 0, #grp := '') init
group by dep;
See SQL fiddle
or db-fiddle.
Make sure to put your table name in the from clause, and to use the actual field you want to group by. If you have multiple fields to group by, the expression assigned to #i will need to change. You could for instance concatenate the values that define a group.
By using a separator of two characters you ensure to have a space between each name.
Try this:
SET #x:=0;
SELECT
GROUP_CONCAT(CONCAT(#x:=#x+1, '.', s.empnam) SEPARATOR ', ') AS emps, #x:=0
< tables >
< filters >
Related
in mySQL query is an
GROUP_CONCAT(fieldname SEPARATOR ', ')
But field with should not be too long.
Therefore I want a different Separator after x datasets (i.e. each 3rd separator should be '\n')
I would be happy to get help for this.
Thanks!
This is a real pain. One method uses lead() to bring three values together and then filter the values to every third one:
select x,
group_concat(col_3 separator '; ')
from (select t.x,
concat_ws(', ',
col,
lead(col, 1) over (order by ?),
lead(col, 2) over (order by ?)
) as col_3
row_number() over (partition by x order by ?) as seqnum
from t
) t
where mod(seqnum, 3) = 1
group by x;
If you want other aggregations, you can filter in the group_concat() instead:
select x,
group_concat(case when mod(seqnum, 3) = 1 then col_3 end separator '; ')
This below was originally written for MySQL, with the #rownumber is defined as a scalar-variable.
SELECT CONCAT( z.expected, IF(z.got-1>z.expected, CONCAT(' thru ',z.got-1), '') AS `Missing Receipt ID`
FROM (SELECT #rownum \\:= #rownum+1 AS expected,
IF(#rownum=recpt_id, 0, #rownum \\:= recpt_id) AS got
FROM
(SELECT #rownum \\:= (SELECT MIN(CAST(recpt_id AS SIGNED))-1 FROM report_receipt
WHERE outlet_desc IN "+Branch+" )
) AS a
JOIN report_receipt r
ON r.outlet_desc IN "+Branch+"
ORDER BY CAST(recpt_id AS SIGNED)
) AS z
WHERE z.got!=0
I am trying to create the same query but for the SQL Server platform. I am aware that in MSSQL, we can use the ROW_NUMBER function and then have a OVER(ORDER ) clause at a place where the ROW_NUMBER is called. The query is also going to be used in native query format in our Java code.
So far, after some minor syntax adjustments, I have reached to this current state in MSSQL but with some uncertainties:
SELECT CONCAT( z.expected, CASE WHEN z.got-1>z.expected THEN CONCAT(' thru ',z.got-1) ELSE '' END) AS [Missing Receipt ID]
FROM
(SELECT #rownum \:= #rownum+1 AS expected,
IF(#rownum=recpt_id, 0, #rownum \:= recpt_id) AS got
FROM (SELECT ROW_NUMBER() OVER(ORDER BY CAST(recpt_id AS INT))) \:= (SELECT MIN(CAST(recpt_id AS INT))-1 FROM report_receipt
WHERE outlet_desc IN "+Branch+" )
) AS a
JOIN report_receipt r
ON r.outlet_desc IN ('MY011')) AS z
WHERE z.got!=0
What I'm unsure but looking for answers would be:
If there's is only one place ORDER clause is placed, do we just place it inside the innermost query while for other places we have to use only ROW_NUMBER() OVER(ORDER BY SELECT NULL)?
If the query is considered having unnecessary additional nesting, what is the other alternative without having to call too many ROW_NUMBER functions repeatedly?
Thanks in advance.
Note: The '\' is an escape character added for the Java code.
My Following Query gives the result according to my record but what i have the problem is my query is generating some different serial number .Anyways i need the serial number to start from 1 but this query is join query is shows different serial number like i can say during joining it skips the series .
Kindly help to generate the series from 1 to number of rows from db without skipping the series in between. Thanks in Advance
SELECT
#a:=#a+1 sno,
p.po_no as id,
DATE_FORMAT(p.po_date, '%d-%m-%Y') as po_date,
p.customer,
p.cust_po as po_no,
p.tot_ord_qty,
DATE_FORMAT(p.delivery_date, '%d-%m-%Y') AS delivery_date,
p.dc_status,
p.inv_status,
p.tot_dc_qty,
p.tot_inv_qty,
COALESCE(GROUP_CONCAT(distinct d.dc_no SEPARATOR ', '), 0) as dc,
GROUP_CONCAT( d.active SEPARATOR ', ') as status
FROM (SELECT #a:= 0) AS a, po_header p
LEFT JOIN dc_details d
ON p.cust_po = d.cust_po
group by
p.cust_po
Result:
The reason you're skipping numbers is because of GROUP BY. Grouping is done after the serial numbers are generated, so it combines all the rows with the same cust_po and you only see one of the serial numbers.
Move the grouping into a subquery and add the serial numbers in the main query.
SELECT #a := #a+1 AS sno, t.*
FROM (SELECT #a := 0) AS a
CROSS JOIN (
SELECT p.po_no as id,
DATE_FORMAT(p.po_date, '%d-%m-%Y') as po_date,
p.customer,
p.cust_po as po_no,
p.tot_ord_qty,
DATE_FORMAT(p.delivery_date, '%d-%m-%Y') AS delivery_date,
p.dc_status,
p.inv_status,
p.tot_dc_qty,
p.tot_inv_qty,
COALESCE(GROUP_CONCAT(distinct d.dc_no SEPARATOR ', '), 0) as dc,
GROUP_CONCAT( d.active SEPARATOR ', ') as status
FROM po_header AS p
LEFT JOIN dc_details AS d ON p.cust_po = d.cust_po
GROUP BY p.cust_po
ORDER BY p.cust_po) AS t
i have the next sub-query:
SELECT SUBSTRING(
(SELECT GROUP_CONCAT(DISTINCT PT.Factura SEPARATOR '|')
FROM darwin.vt_partidas PT
WHERE PT.Pedimento = P.ID)
,1,30) AS 'Resultado'
FROM darwin.vt_pedimentos P WHERE P.ID=130
I need to concat all results separated with | until i reach 130 characters, but my problem is that if at the end a result doesn't fit example:
i get the first 30 characters but the last result doesn't fit, i get:
result1|result2|result3|result
and i want this:
result1|result2|result3
(if the result doesn't fit, remove all characters from that result)
Thank you guys
Try this
Updated your GROUP_CONCAT and added another step to remove irrelevant data exceeding the 30 max length
SELECT
#str:= left(GROUP_CONCAT( DISTINCT PT.Factura SEPARATOR '|'), 30)
FROM
vt_pedimentos P
INNER JOIN
vt_partidas PT
ON PT.Pedimento = P.ID
WHERE
P.ID = 130;
-- to check whether the last or truncated text exists in the table otherwise remove
select
#str:= left(#str,
(
length(#str) - length(reverse(left(reverse(#str), locate('|', reverse(#str)) - 1)))
)
- 1)
FROM
vt_pedimentos P
where
NOT EXISTS
(
select
1
from
vt_partidas PT
where
PT.Factura = reverse(left(reverse(#str), locate('|', reverse(#str)) - 1))
)
and P.ID = 130;
further enhancement - have it to one sql statement
String manipulation is not a forte of SQL expressions.
But something like this should do it:
SELECT
IF(CHAR_LENGTH( GROUP_CONCAT(DISTINCT PT.Factura SEPARATOR '|') ) < 130
, GROUP_CONCAT(DISTINCT PT.Factura SEPARATOR '|')
, SUBSTRING_INDEX(
SUBSTR( GROUP_CONCAT(DISTINCT PT.Factura SEPARATOR '|') ,1,130)
, '|'
, CHAR_LENGTH( SUBSTR( GROUP_CONCAT(DISTINCT PT.Factura SEPARATOR '|') ,1,130) )
-CHAR_LENGTH(REPLACE(SUBSTR( GROUP_CONCAT(DISTINCT PT.Factura SEPARATOR '|') ,1,130),'|',''))
)
)
That's fairly complicated. It will be easier to decipher if we replace the GROUP_CONCAT expression with a placeholder. Let's have res represent GROUP_CONCAT(DISTINCT PT.Factura SEPARATOR '|') expression.
SELECT
IF(CHAR_LENGTH( res ) < 130
, res
, SUBSTRING_INDEX(
SUBSTR( res ,1,130)
, '|'
, CHAR_LENGTH( SUBSTR( res ,1,130) )
-CHAR_LENGTH(REPLACE(SUBSTR( res ,1,130),'|',''))
)
)
Still ugly, but better. Let's break that down.
If the number of characters in res is less than 130, we're done. Just return res.
Otherwise, we need to trim res to 130 characters, we can use SUBSTRING function to do that.
Now, we want to trim the last | and the following characters. To do that, we can get a count of the number | separator characters. Then we know which one the last one is.
(We can get a count of the separator characters by replacing all separator characters with an empty string, then getting the length of that string, and subtracting that from the length of the original string. The difference is the total length of the removed separator characters.
Then we can use that difference in a SUBSTRING_INDEX function to return all of the the characters before the last separator.
It's not a pretty solution. But it does implement an algorithm that satisfies the specification.
Say I have a table as shown below
Product_Id Description
BX01 Desc 1
BxX1 Desc 2
Dss3 Desc 3
HHXY Desc 4
I want the result exactly like: 1 - BX01, 2 - BxX1, 3 - Dss3, 4 - HHXY
I have this query:
DECLARE #ProID VARCHAR(8000)
SELECT #ProID = COALESCE(#ProID + ' - ', '') + Product_Id FROM TABLE
SELECT #ProID
but the return values is only :
BX01,- BxX1,- Dss3,- HHXY.
The counting is lacking.
How to do that?
thanks
Try this instead:
SELECT GROUP_CONCAT(
CONCAT( #rownum := #rownum + 1, '-', Product_Id )
SEPARATOR ','
) AS data
FROM table, ( select #rownum := 0 ) r
SQL Fiddle Demo
This will build the result you wanted by grouping all the column data and adding a counter before it, separating each data with a comma.
Note: You can remove , ( select #rownum := 0 ) r though. Depends on what you want.
In mysql it would be ,
SET #a:=0;
SELECT CONCAT( #a:=#a+1,'-',Product_Id) FROM TABLE ;