group_concat missing on of the group - mysql

I have two tables, the first puts puts pairs of people into a group with fldPairNum and a second table which collects scores for each individual person.
The problem I have is that if only one of the pair has submitted a score, then only their name appears in the 'nameOfPair' column, but I really need both names. What can I do to fix this?
SELECT
group_concat(DISTINCT `delegate`.`fldFirstName`,' ',`delegate`.`fldSurname` SEPARATOR ' and ') AS 'nameOfPair',
Sum(`data`.`fldScore`) AS 'totalScore'
FROM
`data`
Inner Join `delegate` ON `data`.`fldDelegateID` = `delegate`.`fldID`
WHERE
`delegate`.`fldCategory` > '0'
AND
`delegate`.`fldPairNum` > '0'
GROUP BY
`delegate`.`fldPairNum`
Many thanks
Dave

SELECT GROUP_CONCAT(DISTINCT
`delegate`.`fldFirstName`, ' ', `delegate`.`fldSurname`
SEPARATOR
' and ') AS 'nameOfPair',
SUM(`data`.`fldScore`) AS 'totalScore'
FROM `delegate`
LEFT JOIN `data`
ON `data`.`fldDelegateID` = `delegate`.`fldID`
WHERE `delegate`.`fldCategory` > '0'
AND `delegate`.`fldPairNum` > '0'
GROUP BY `delegate`.`fldPairNum`

Related

Different MYSQL Output with same Query

I have three tables:
events:
id | name
-------------------
1 | Test
2 | Another test
persons:
type | type_id | name | user_id
------------------------------------------------
event_organizer | 318 | NULL | 22
event_owner | 318 | Rob | NULL
event_owner | 318 | NULL | 6
user:
id | forname | surname
--------------------------
6 | Peter | Watson
7 | Chris | Brown
22 | Charlie | Teck
(Of course, the tables are much bigger than that, I just copied the relevant parts.)
This query:
SELECT event.*,
IF(persons.type='event_organizer', CONCAT_WS(', ', GROUP_CONCAT(forname, ' ', surname SEPARATOR ', '), GROUP_CONCAT(persons.name SEPARATOR ', ')), NULL) AS organizer_names,
IF(persons.type='event_owner', GROUP_CONCAT(forname, ' ', surname SEPARATOR ', '), NULL) AS owner_names
FROM event
LEFT JOIN persons ON persons.type_id = event.id AND (persons.type = 'event_owner' OR persons.type = 'event_organizer')
LEFT JOIN user ON user.id = persons.user_id
WHERE event.id=?
should output me all event data and the names of the owner and the organizer. The output I get is:
Array ( [id] => 318 [name] => Test [organizer_names] => Peter Watson, Rob, Charlie Teck [owner_names] => )
I don't get why owner_names is always empty. If I remove the organizer_names part:
SELECT event.*,
IF(persons.type='event_owner', GROUP_CONCAT(forname, ' ', surname SEPARATOR ', '), NULL) AS owner_names
FROM event
LEFT JOIN persons ON persons.type_id = event.id AND persons.type = 'event_owner'
LEFT JOIN user ON user.id = persons.user_id
WHERE event.id=?
Than I get the right owner (Rob and Peter Watson). I can also change it to:
SELECT event.*,
IF(persons.type='event_organizer', CONCAT_WS(', ', GROUP_CONCAT(forname, ' ', surname SEPARATOR ', '), GROUP_CONCAT(persons.name SEPARATOR ', ')), NULL) AS organizer_names,
IF(persons.type='event_owner', GROUP_CONCAT(forname, ' ', surname SEPARATOR ', '), NULL) AS owner_names
FROM event
LEFT JOIN persons ON persons.type_id = event.id AND persons.type = 'event_owner'
LEFT JOIN user ON user.id = persons.user_id
WHERE event.id=?
And this is working right too. So it seems that the second OR condition of the LEFT JOIN is destroying my owners :(
The reverse test (with same condition and without organizer_names):
SELECT event.*,
IF(persons.type='event_owner', GROUP_CONCAT(forname, ' ', surname SEPARATOR ', '), NULL) AS owner_names
FROM event
LEFT JOIN persons ON persons.type_id = event.id AND (persons.type = 'event_owner' OR persons.type = 'event_organizer')
LEFT JOIN user ON user.id = persons.user_id
WHERE event.id=?
brings also no owner_names output.
Where is my mistake?
Using GROUP BY aggregate functions without a GROUP BY clause is valid SQL. The standard says a single group is created from all the selected rows.
For this specific question, the appropriate GROUP BY clause would be GROUP BY event.id. However, it doesn't help because the WHERE clause already filters only the rows having a single value for event.id.
To debug the query remove all the aggregate functions in the SELECT clause and check that the correct rows are selected:
SELECT event.*, persons.*, users.*
FROM event
LEFT JOIN persons ON persons.type_id = event.id AND (persons.type = 'event_owner' OR persons.type = 'event_organizer')
LEFT JOIN user ON user.id = persons.user_id
WHERE event.id=?
Conceptually, this is the first step executed by MySQL when it runs your original query. In practice, some shortcuts are taken and optimizations are done and the result set you get is not generated completely; but this is how it works in theory.
Most probably, the rows are correctly joined and filtered and the problem is in the way the values are aggregated.
Let's try to analyze how MySQL computes this expression:
IF(
persons.type='event_owner',
GROUP_CONCAT(forname, ' ', surname SEPARATOR ', '),
NULL
) AS owner_names
It uses all the rows selected by the query displayed above to compute a single value for the above expression. If you run the query you can see it produces rows that contain both 'event_owner' and 'event_organizer' in the persons.type column.
From the inside out, it uses all the values returned in the forname and surname columns to compute GROUP_CONCAT(forname, ' ', surname SEPARATOR ', ') then it uses one value from the persons.type column to check the IF() condition. But which one?
It is entitled to pick whatever value it wants for it from all the possible values of the persons.type column selected on the previous step. If, by chance, it uses 'event_owner' it returns the value you expect and you think the query is correct. When it uses 'event_organizer', well, you are puzzled and ask on StackOverflow ;)
The correct expression of owner_names is:
GROUP_CONCAT(
IF( # for each row
persons.type='event_owner',
CONCAT(forname, ' ', surname), # full name for owner
NULL # nothing for organizer
) # owner name or NULL
SEPARATOR ', ' # concatenate all using separator
) as owner_names
In a similar fashion you can compute the organizer names.
I can't test this - SQLFiddle appears broken right now - but I think you need to explicitly join organizers and owners as separate joins. Something like:
SELECT event.*,
organizers.*,
IF(organizers.type='event_organizer', CONCAT_WS(', ', GROUP_CONCAT(forname, ' ', surname SEPARATOR ', '), GROUP_CONCAT(persons.name SEPARATOR ', ')), NULL) AS organizer_names,
owners.* ,
IF(owners.type='event_owner', GROUP_CONCAT(forname, ' ', surname SEPARATOR ', '), NULL) AS owner_names
FROM event
LEFT JOIN persons organizers ON organizers.type_id = event.id AND organizers.type = 'event_organizer'
left join persons owners on owners.type_id = event.id and owners.type = 'event_owner'
LEFT JOIN user ON user.id = owners.user_id
left join user u2 on u2.id = organizers.user_id
WHERE event.id=?;

How to consolidate in a query?

I need some help writing a query to consolidate names into a list that are associated with a foreign key. Here's my current query,
select distinct concat(c_first, ' ', c_last) as name, pmt_no
from disbursements d
left join contacts c on c.c_no = d.b_no
where d.ba_no = 1
My result set looks like this
Louis Vaz, 586014
Antionette An, 690682
Brian Cald, 690682
Mark Brian, 3233902
My desired outcome is
Louis Vaz, 586014
Antionette An - Brian Cald, 690682
Mark Brian, 3233902
Please note that both the people with pmt_no 690682 are now joined together with a '-' separating them.
You can use the GROUP_CONCAT() function to achieve what you want:
select group_concat(distinct concat(c_first, ' ', c_last) SEPARATOR ' - ') as name, pmt_no
from disbursements d
left join contacts c on c.c_no = d.b_no
where d.ba_no = 1
group by pmt_no
I think you want group_concat() along with a group_by:
select group_concat(c_first, ' ', c_last separator ' - ') as names,
pmt_no
from disbursements d left join
contacts c
on c.c_no = d.b_no
where d.ba_no = 1
group by pmt_no;

MySQL: handling conditional values within GROUP_CONCAT

I'm trying retrieve data using a LEFT JOIN and GROUP_CONCAT(). However, where values aren't present, I need something to represent the lack of data, or a method of identification for each, so that I can do something with them in the application.
CONCAT_WS(0x1D,
GROUP_CONCAT(DISTINCT bcod1.value
SEPARATOR 0x1F),
GROUP_CONCAT(DISTINCT bcod2.value
SEPARATOR 0x1F),
GROUP_CONCAT(DISTINCT bcod3.value
SEPARATOR 0x1F),
GROUP_CONCAT(DISTINCT bcod4.value
SEPARATOR 0x1F)) AS clients_options
Here, the four instances of GROUP_CONCAT() relate to either an INNER JOIN or a LEFT JOIN in a search performed by the user:
INNER JOIN
bookings_clients_options bco1 ON (bco1.client_id = '3')
INNER JOIN
bookings_clients_options_data bcod1 ON (bco1.name = 'unit_code')
AND (bco1.bookings_client_option_id = bcod1.bookings_client_option_id)
AND (bcod1.value REGEXP ('.*'))
AND (bcod1.booking_attendee_id = bookings_attendees.booking_attendee_id)
INNER JOIN
bookings_clients_options bco2 ON (bco2.client_id = '3')
INNER JOIN
bookings_clients_options_data bcod2 ON (bco2.name = 'creditor_ev_number')
AND (bco2.bookings_client_option_id = bcod2.bookings_client_option_id)
AND (bcod2.value REGEXP ('.*'))
AND (bcod2.booking_attendee_id = bookings_attendees.booking_attendee_id)
INNER JOIN
bookings_clients_options bco3 ON (bco3.client_id = '3')
LEFT JOIN
bookings_clients_options_data bcod3 ON (bco3.name = 'purchase_order_number')
AND (bco3.bookings_client_option_id = bcod3.bookings_client_option_id)
AND (bcod3.booking_attendee_id = bookings_attendees.booking_attendee_id)
INNER JOIN
bookings_clients_options bco4 ON (bco4.client_id = '3')
INNER JOIN
bookings_clients_options_data bcod4 ON (bco4.name = 'purchase_order_booking')
AND (bco4.bookings_client_option_id = bcod4.bookings_client_option_id)
AND (bcod4.value REGEXP ('Y'))
AND (bcod4.booking_attendee_id = bookings_attendees.booking_attendee_id)
Here, bcod3.value is where the results "collapse" in that if there's no value for bcod3.value but a value for bcod4.value, then bcod4.value drops into the space for bcod3.value.
As you can see, each of these columns has a name, but having tried...
GROUP_CONCAT(DISTINCT bcod" . $x . ".value SEPARATOR 0x1F) AS unit_code
... I got an error from the surrounding CONCAT_WS().
I tried...
IF(bcod" . $x . ".value = '', 'QQQ', GROUP_CONCAT(DISTINCT bcod" . $x . ".value SEPARATOR 0x1F))
... but that doesn't appear to do anything.
I tried...
GROUP_CONCAT(DISTINCT IF(bcod" . $x . ".value = 0, 'QQQ', bcod" . $x . ".value) SEPARATOR 0x1F)
.. and while it did grab the instances of bcod3.value, it also grabbed some of the instances of bcod1.value that have values which are not '0'.
I'm not 100% sure I understand your question, but I think I do. Sample data and desired results are worth 1000 words of explanation.
If the problem is that NULL or blank values are being ignored, then just do:
CONCAT_WS(0x1D,
coalesce(GROUP_CONCAT(DISTINCT bcod1.value SEPARATOR 0x1F), ''),
coalesce(GROUP_CONCAT(DISTINCT bcod2.value SEPARATOR 0x1F), ''),
coalesce(GROUP_CONCAT(DISTINCT bcod3.value SEPARATOR 0x1F), ''),
coalesce(GROUP_CONCAT(DISTINCT bcod4.value SEPARATOR 0x1F), '')
) AS clients_options
It is generally considered a benefit that group_concat() and concat_ws() ignore NULL values. However, if you want to do something with them, just use coalesce().

MySQL doesn't group 'text' cell correctly?

I have code that doesn’t work correctly:
SELECT tbl2.entry_id, tbl2.field_id_2, group_concat(DISTINCT tbl2.tag_name SEPARATOR ', ') FROM
(SELECT e.entry_id, e.field_id_2, tbl1.tag_name
FROM (exp_channel_data e)
LEFT JOIN (SELECT te.entry_id, tag.tag_id, tag.tag_name FROM exp_tag_entries te, exp_tag_tags tag WHERE te.tag_id = tag.tag_id) tbl1
ON (e.entry_id = tbl1.entry_id)
WHERE e.entry_id IN (371, 373)) tbl2
GROUP BY tbl2.entry_id, tbl2.field_id_2
It returns:
371;<p>...</p>;digital strategy
371;<p>...</p>;seo
371;<p>...</p>;inbound marketing
373;<p>...</p>;infographic, strategy, social media
but shoud be:
371;<p>...</p>;digital strategy, seo, inbound marketing
373;<p>...</p>;infographic, strategy, social media
Please, help me solve this problem. Type of 'field_id_2' cell is text, that have multiline value.
For example, row with entry_id = 371 is here on PasteBin.
Try this
GROUP BY tbl2.entry_id
instead of
GROUP BY tbl2.entry_id, tbl2.field_id_2
and change this
group_concat(DISTINCT tbl2.tag_name SEPARATOR ', ')
to
group_concat(tbl2.tag_name SEPARATOR ', ')

MySQL - Optimization select query

My query is taking 5 seconds to finish but it sounds to much for something so basic. I did an explain query:
https://www.dropbox.com/s/18pedm9n5fssz4e/localhost%20_%20localhost%20_%20logrede%20_%20phpMyAdmin%203.4.11.pdf
It is possible to you to help me optimize it?
Thanks :)
I've restructured the query using JOINs instead of a less readable WHERE clause. I see everything is based on the primary "material" table. So, my first suggestion is to use the "STRAIGHT_JOIN" clause to tell MySQL to do the query in the order you have listed.... otherwise, sometimes it tries to think for you and do some things in what could be an alternate "primary" table driving the query. All your other tables are basically "lookup values" tables.
Secondly... On each of the tables, I would specifically have the following indexes on the tables. I'm sure you already had on the primary keys of each table... but since your GROUP_CONCAT are getting a corresponding descriptive field from the table, I would include that column in the index. Here's the other reason... When a query is run and using the index, if the query does not have to go back to the raw table for the "other" columns, it doesn't have to. So, by having the description as part of the index, it can do the join AND get the description both from the index...
I would have an index on
material ( cdomaterial )
cliente ( idcliente, nome )
classeobra ( idclasse, sigla )
dma ( iddma, nome )
desenho( iddesenho, nome )
fornecedor ( idfornecedor, nome )
modelo ( idmodelo, nome )
fabricante ( idfabri, nome )
fornecido( codmaterial, preco )
unidade( idunidade, sigla )
tipofornecimento( idtipoforn, sigla )
So, the above said, I would run the following query...
SELECT STRAIGHT_JOIN
material.codmaterial,
cliente.nome as cliente,
tipofornecimento.sigla as fornecimento,
unidade.sigla as unidade,
GROUP_CONCAT(DISTINCT classeobra.sigla ORDER BY classeobra.idclasse SEPARATOR ', ') as classeobra,
GROUP_CONCAT(DISTINCT dma.nome ORDER BY dma.iddma SEPARATOR ', ') as dma,
GROUP_CONCAT(DISTINCT desenho.nome ORDER BY desenho.iddesenho SEPARATOR ', ') as desenho,
GROUP_CONCAT(DISTINCT fornecedor.nome ORDER BY fornecedor.idfornecedor SEPARATOR ', ') as fornecedor,
GROUP_CONCAT(DISTINCT modelo.nome ORDER BY modelo.idmodelo SEPARATOR ', ') as modelo,
GROUP_CONCAT(DISTINCT fabricante.nome ORDER BY fabricante.idfabri SEPARATOR ', ') as marca,
GROUP_CONCAT(DISTINCT fornecido.preco ORDER BY fornecido.preco SEPARATOR ', ') as preco
FROM
material
JOIN unidad
ON material.idunidade = unidade.idunidade
JOIN requisitado
ON material.codmaterial = requisitado.codmaterial
JOIN cliente
ON requisitado.idcliente = cliente.idcliente
JOIN tipofornecimento
ON requisitado.idtipoforn = tipofornecimento.idtipoforn
JOIN possuimodelo
ON material.codmaterial = possuimodelo.codmaterial
JOIN modelo
ON possuimodelo.idmodelo = modelo.idmodelo
JOIN pertence,
ON modelo.idmodelo = pertence.idmodelo
JOIN fabricante
ON pertence.idfabri = fabricante.idfabri
JOIN utilizadoclasseobra
ON material.codmaterial = utilizadoclasseobra.codmaterial
JOIN classeobra
ON utilizadoclasseobra.idclasse = classeobra.idclasse
JOIN possuidma
ON material.codmaterial = possuidma.codmaterial
JOIN dma
ON possuidma.iddma = dma.iddma
JOIN fornecido
ON material.codmaterial = fornecido.codmaterial
JOIN fornecedor
ON fornecido.idfornecedor = fornecedor.idfornecedor
JOIN possuidesenho
ON material.codmaterial = possuidesenho.codmaterial
JOIN desenho
ON possuidesenho.iddesenho = desenho.iddesenho
GROUP BY
material.codmaterial,
cliente.nome
ORDER BY
material.codmaterial ASC,
cliente.nome ASC ;