MySQL Join Query Autogenerate Serial Number - mysql

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

Related

mySQL GROUP_CONCAT different Separator each x results

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 '; ')

Keep ORDER BY after UNION [duplicate]

This question already has answers here:
How can I order entries in a UNION without ORDER BY?
(7 answers)
Closed 4 years ago.
I have a problem to solve.
I have a table Occupationswith Name and Occupation.
My task is to:
1.Query an alphabetically ordered list of all names in OCCUPATIONS, immediately followed by the first letter of each profession as a parenthetical (i.e.: enclosed in parentheses). For example: AnActorName(A), ADoctorName(D), AProfessorName(P), and ASingerName(S).
query the number of ocurrences of each occupation in OCCUPATIONS. Sort the occurrences in ascending order, and output them in the following format:
There are a total of [occupation_count] [occupation]s.
If more than one Occupation has the same they should be ordered alphabetically.
I am almost done with the query
SELECT TEMP.CON1
FROM (
SELECT NAME, CONCAT(NAME,'(', LEFT(OCCUPATION, 1),')') AS CON1
FROM OCCUPATIONS
ORDER BY NAME
) AS TEMP
UNION
SELECT TEMP2.CON2
FROM (
SELECT COUNT(*) AS NR, CONCAT('THERE ARE A TOTAL OF ', COUNT(OCCUPATION),' ', OCCUPATION, 's') AS CON2
FROM OCCUPATIONS
GROUP BY OCCUPATION
ORDER BY NR, OCCUPATION
) AS TEMP2
but I don't know how to keep the order of the first section after the two sections are united.
If anyone knows the answer I would be superglad for sharing.
SELECT CON
FROM
(
SELECT 1 as SEQ, 0 as NR, NAME ,TEMP.CON1 as CON
FROM (
SELECT NAME, CONCAT(NAME,'(', LEFT(OCCUPATION, 1),')') AS CON1
FROM OCCUPATIONS
ORDER BY NAME -- don't need
) AS TEMP
UNION
SELECT 2, NR, null, TEMP2.CON2
FROM (
SELECT COUNT(*) AS NR, CONCAT('THERE ARE A TOTAL OF ', COUNT(OCCUPATION),' ', OCCUPATION, 's') AS CON2
FROM OCCUPATIONS
GROUP BY OCCUPATION
ORDER BY NR, OCCUPATION -- don't need
) AS TEMP2 ) T
ORDER BY SEQ, NR, NAME, CON
UNION ALL would keep the order, and would work just as well in this case as your result sets are totally different.
The difference between union and union all is that UNION removes duplicates, which causes reordering, where UNION ALL simply adds the next set of results to the end of the existing result set. Since you are manipulating the strings on both selects, there is no real chance of there being duplicates in the results between TEMP and TEMP2. This will also get rid of some of the cost of the query, since UNION is more expensive than UNION ALL, as it needs to check for duplicates.
SELECT TEMP.CON1
FROM (
SELECT NAME, CONCAT(NAME,'(', LEFT(OCCUPATION, 1),')') AS CON1
FROM OCCUPATIONS
ORDER BY NAME
) AS TEMP
UNION ALL
SELECT TEMP2.CON2
FROM (
SELECT COUNT(*) AS NR, CONCAT('THERE ARE A TOTAL OF ', COUNT(OCCUPATION),' ', OCCUPATION, 's') AS CON2
FROM OCCUPATIONS
GROUP BY OCCUPATION
ORDER BY NR, OCCUPATION
) AS TEMP2
This being said, the way I read the instructions, I wouldn't expect both results to be desired in the same result set.

SQL - UNION does not order second SELECT

I`m trying to solve this challenge:
https://www.hackerrank.com/challenges/the-pads
My solution is this in MySQL:
(SELECT CONCAT(Name,'(',SUBSTR(Occupation,1,1),')') FROM Occupations ORDER BY Name)
UNION
(SELECT CONCAT('There are total ', COUNT(Occupation), ' ',LOWER(Occupation),'s.') AS total FROM Occupations
GROUP BY Occupation
ORDER BY total);
However it fails to ORDER BY total.
Ashley(P)
Samantha(A)
Julia(D)
Britney(P)
Maria(P)
Meera(P)
Priya(D)
Priyanka(P)
Jennifer(A)
Ketty(A)
Belvet(P)
Naomi(P)
Jane(S)
Jenny(S)
Kristeen(S)
Christeen(S)
Eve(A)
Aamina(D)
There are total 4 actors.
There are total 3 doctors.
There are total 7 professors.
There are total 4 singers.
If I only run
SELECT CONCAT('There are total ', COUNT(Occupation), ' ',LOWER(Occupation),'s.') AS total FROM Occupations
GROUP BY Occupation
ORDER BY total
it does order:
There are total 3 doctors.
There are total 4 actors.
There are total 4 singers.
There are total 7 professors.
The result gets ordered by the final ORDER BY clause. This is ORDER BY total, i.e. by the first column. (You only give this name in the second part of UNION, which would probably not work in another DBMS. You should name the columns you select in a UNION query in the first part.)
You want to get names first, then the aggregates. Then you want names alphabetically, aggregates by count (i.e. not alphabetically, not 1 -> 10 -> 11 -> 2 -> 20 ..., but 1 -> 2 -> 10 -> 11 -> 20 ...) then by job name. You can create sortkeys for this task. I assume you really want UNION ALL, not UNION. If I am wrong, change it :-)
SELECT txt
FROM
(
SELECT
CONCAT(Name, '(', SUBSTR(Occupation, 1, 1), ')') as txt,
1 as sortkey1,
CONCAT(Name, '(', SUBSTR(Occupation, 1, 1), ')') as sortkey2
FROM Occupations
UNION ALL
SELECT
CONCAT('There are total ', COUNT(Occupation), ' ', LOWER(Occupation), 's.') AS txt,
2 + COUNT(Occupation) as sortkey1,
LOWER(Occupation) as sortkey2
FROM Occupations
GROUP BY Occupation
) data
ORDER BY sortkey1, sortkey2;
That is correct. The ordering of the result set is based only on the outermost order by. This is true for union as for other operations.
(SELECT CONCAT(Name,'(', SUBSTR(Occupation,1,1),')') AS total
FROM Occupations
ORDER BY Name
)
UNION ALL
(SELECT CONCAT('There are total ', COUNT(Occupation), ' ', LOWER(Occupation), 's.') AS total
FROM Occupations
GROUP BY Occupation
)
ORDER BY (CASE WHEN total LIKE 'There are total%' THEN 1 ELSE 0 END),
Total;
This assumes that Name never starts with 'There are total', which seems likely.
#gordon answer is fine
But for a more generic case you have to create a "dummy" field to separate each group.
(SELECT CONCAT(Name,'(', SUBSTR(Occupation,1,1),')') as Name,
0 as dummy
FROM Occupations
)
UNION ALL
(SELECT CONCAT('There are total ', COUNT(Occupation), ' ', LOWER(Occupation), 's.') AS Name,
1 as dummy
FROM Occupations
GROUP BY Occupation
)
ORDER BY dummy, name
Based on the answers and my best understanding I came with this solution that works for this case:
(SELECT CONCAT(Name,'(',SUBSTR(Occupation,1,1),')') as total FROM Occupations ORDER BY Name)
UNION ALL
(SELECT CONCAT('There are total ', COUNT(Occupation), ' ',LOWER(Occupation),'s.') AS total FROM Occupations
GROUP BY Occupation)
ORDER BY total;
This worked for me.
you can give two different select queries whith out UNION/ UNION ALL.
SELECT Concat(NAME, '(', Substr(occupation, 1, 1), ')') AS Result
FROM occupations
ORDER BY NAME;
SELECT Concat('There are a total of ', Count(*), ' ', Lower(occupation), 's.')
FROM occupations
GROUP BY occupation
ORDER BY Count(*), occupation;

GROUP_CONCAT numbering

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 >

MySQL GROUP BY each comma separated value

Before anyone comments, I did not design this database with comma separated values :)
I have spent time trying to find the answer but all I could find was GROUP_CONCAT() which seemed to do the opposite of what I wanted.
I would like to GROUP BY each of the values within the comma separated value field.
SELECT round(avg(DATEDIFF( dateClosed , dateAded ) * 1.0), 2) AS avg, department
FROM tickets GROUP BY assignedto
the assignedto field is the comma separated value field
row1 54,69,555
row2 54,75,555
row3 75,555
DESIRED OUTPUT: an average rounded figure for each value in assignedto field grouped.
EDIT - TRYING TO TAKE THIS TO THE NEXT LEVEL:
I want to include the ticket answer table to get the first response for that ticket, use its datetime field to work out the average response time for each user.
SELECT a.id as theuser, round(avg(DATEDIFF( ta.dateAded , t.dateAded ) * 1.0), 2) as avg
FROM tickets t join
mdl_user a
on find_in_set(a.id, t.assignedto) > 0
INNER JOIN (SELECT MIN(ta.dateAded) as started FROM ticketanswer GROUP BY ta.ticketId) ta ON t.id = ta.ticketId
GROUP BY a.id ORDER BY avg ASC
Yuck. You can do this, assuming you know the maximum number of assignments. Here is an approach:
select substring_index(substring_index(assignedto, ',', n.n), ',', -1) as assignedto,
round(avg(DATEDIFF( dateClosed , dateAded ) * 1.0), 2) as avg
from tickets t join
(select 1 as n union all select 2 union all select 3)
on length(assignedto) - length(replace(assignedto, ',', '')) < n.n
group by substring_index(substring_index(assignedto, ',', n.n), ',', -1);
Or, an easier way if you have a list of assigned values, say in an AssignedTo table:
select a.assignedto, round(avg(DATEDIFF( dateClosed , dateAded ) * 1.0), 2) as avg
from tickets t join
assignedto a
on find_in_set(a.assignedto, t.assignedto) > 0
group by a.assignedto;
I'm sorry you have to deal with this malformed database structure.