Different MYSQL Output with same Query - mysql

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=?;

Related

Find the relationship match in sql

If I got 2 table, which is
How do I get the result of
I know combine the name is using CONCAT_WS function, but I don't understand how can I exchange the row such as the "wife" and "husband" on my output.
You should join the client table two time (of of each related part in row) using tablename alias
select
concat(c1.client_firstname, ' ' , c1.client_lastname) as A_Name
, r.rel_client1_state as A_State
, concat(c2.client_firstname, ' ' , c2.client_lastname) as B_Name
, r.rel_client2_state as B_State
from relationships as r
inner join client as c1 on r.rel_client1_id = c1.client_id
inner join clinet as c2 on r.rel_client2_id = c2.client_id
SELECT CONCAT_WS(' ', c1.client_firstname, c1.client_lastname) as c1name,
r.rel_client1_state, CONCAT_WS(' ', c2.client_firstname, c2.client_lastname) as c2name,
r.rel_client2_state FROM client c1
JOIN relationship r ON c1.client_id = r.rel_client1_id
JOIN client c2 ON c2.client_id = r.rel_client2_id WHERE 1

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;

Multiple answers on one line

I have the following tables:
matters(matterid, mattername, refno)
mattersjuncstaff(junked, matterid, staffid, lead)
staff(staffid, staffname)
A matter may have a number of staff associated with it and a number of those staff will be marked as ‘leads’ i.e. they will have a ‘Y’ in the ‘lead’ field.
I wish to show a table that has a list of matters, the matter name and ref no and those staff marked as leads, ideally in a single row. So it would look something like:
reference | mattername | Lead Staff |
ABC1 | matter abc & Co | Fred Smith, Jane Doe, Naomi Watts |
etc
I am using the code below but this only displays one person with the lead field marked Y.
SELECT refno, mattername, matters.matterid, staffname
FROM matters
INNER JOIN matterjuncstaff
USING (matterid)
Inner join staff
using (staffid)
Inner join matterjuncactions
On matterjuncactions.matterid = matters.matterid
WHERE lead = 'Y'
GROUP BY matters.matterid, nickname
Can anyone tell me how I can I get round this?
You want to concatenate values from a join and represent that as a field in the result set. GROUP_CONCAT function is suited for such queries:
SELECT m.matterid, m.refno, m.mattername, GROUP_CONCAT(s.staffname) AS LeadStaff
FROM matters m
LEFT JOIN matterjuncstaff mjs ON mjs.matterid = m.matterid AND lead = 'Y'
LEFT JOIN staff s ON s.staffid = mjs.staffid
GROUP BY m.matterid, m.refno, m.mattername
The join changed to LEFT and lead = 'Y' moved there, otherwise you will lose matters with no lead staffs.
Use INNER JOIN if you only want matters having some lead staff.
I have removed matterjuncactions as you did not give its info.
Use the GROUP_CONCAT() function in mysql to concatenate values from a query into a single string.
For example you could select a row for each matter and append a column with all the concatenated lead staff names as follows:
SELECT m.refno,
m.mattername,
(Select GROUP_CONCAT(distinct staffname SEPARATOR ', ')
from mattersjuncstaff js
join staff s
on s.staffid = js.staffid
where js.lead = 'Y'
and js.matterid = m.matterid) as LeadStaffMembers
FROM matters m
Update
Here is the same example, but with an added column showing staff members that are not the lead.
SELECT m.refno,
m.mattername,
(Select GROUP_CONCAT(distinct staffname SEPARATOR ', ')
from mattersjuncstaff js
join staff s
on s.staffid = js.staffid
where js.lead = 'Y'
and js.matterid = m.matterid) as LeadStaffMembers,
(Select GROUP_CONCAT(distinct staffname SEPARATOR ', ')
from mattersjuncstaff js
join staff s
on s.staffid = js.staffid
where js.lead <> 'Y'
and js.matterid = m.matterid) as NonLeadStaffMembers
FROM matters m

SQL multiple rows as columns (optimizing)

I have a SQL query which gives the correct result, but performs too slow.
The query operates on the following three tables:
customers contains lots of customer data like name, address, phone
etc. To simplify the table i am only using the name.
customdatas contains certain custom (not customer) data. (The
tables are created in software, which is why the plural form is wrong
for this table)
customercustomdatarels associates custom data with a customer.
customers
Id Name (many more columns)
-----------------------------------------------------------------------
8053c6f4c5c5c631054ddb13d9186117 MyCustomer ...
2efd2aa5711ddfade1f829b12dd88cf3 CheeseFactory ...
customdata
id key
-------------------------------------------------
22deb172c1af6e8e245634a751871564 favoritsport
86eea84d296df9309ad6ff36fd7f856e favoritcheese
customercustomdatarels (relation between customer and custom data - with corresponding value)
customer customdata value
-------------------------------------------------------------------------------------
8053c6f4c5c5c631054ddb13d9186117 22deb172c1af6e8e245634a751871564 cycling
8053c6f4c5c5c631054ddb13d9186117 86eea84d296df9309ad6ff36fd7f856e cheddar
2efd2aa5711ddfade1f829b12dd88cf3 22deb172c1af6e8e245634a751871564 football
2efd2aa5711ddfade1f829b12dd88cf3 86eea84d296df9309ad6ff36fd7f856e mouldy
What i want is a table basically consisting of all data in customers with an variable amount of extra columns, corresponding to the custom data specified in customercustomdatarels.
These columns should be defined somewhere and I have therefore created the following table which defines such extra columns and maps them to a key in the customdata table:
test_customkeymapping
colkey customkey
---------------------
1 favoritsport
2 favoritcheese
The result should then be:
Name ExtraColumn_1 ExtraColumn_2
---------------------------------------------
CheeseFactory football mouldy
MyCustomer cycling cheddar
(ExtraColumn_1 is therefore synonym for a customers' favorite sport and ExtraColumn_2 is a synonym for a customers' favorit cheese.)
This result is achieved by executing the following query:
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT('MAX(CASE
WHEN ckm.colkey = ', colkey, ' THEN
(SELECT value FROM customercustomdatarels ccdr2
LEFT JOIN customdatas cd2
ON cd2.id = ccdr2.customdata
WHERE cd2.key = ckm.customkey AND c.Id = ccdr2.customer)
END) AS ', CONCAT('`ExtraColumn_', colkey, '`'))
) INTO #sql
FROM test_customkeymapping;
SET #sql = CONCAT('SELECT c.Name, ', #sql, '
FROM customers c
LEFT JOIN customercustomdatarels ccdr
ON c.Id = ccdr.customer
LEFT JOIN customdatas cd
ON cd.Id = ccdr.customdata
LEFT JOIN test_customkeymapping ckm
ON cd.key = ckm.customkey
GROUP BY c.Id');
PREPARE stmt FROM #sql;
EXECUTE stmt;
This works. But is too slow (for 7000 customers it takes ~10 seconds).
The query was greatly influenced by the solution in this question:
MySQL Join Multiple Rows as Columns
How do I optimize this query?
I don't understand why you are using a subquery in the group_concat() statement. Wouldn't this generate the code that you really want to run?
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT('MAX(CASE WHEN ckm.colkey = ', colkey, ' THEN ccd.value END) AS ',
CONCAT('ExtraColumn_', colkey, ''))
) INTO #sql
FROM test_customkeymapping;
SET #sql = CONCAT('SELECT c.Name, ', #sql, '
FROM customers c
LEFT JOIN customercustomdatarels ccdr
ON c.Id = ccdr.customer
LEFT JOIN customdatas cd
ON cd.Id = ccdr.customdata
LEFT JOIN test_customkeymapping ckm
ON cd.key = ckm.customkey
GROUP BY c.Id');
PREPARE stmt FROM #sql;
EXECUTE stmt;
Note: This is untested, but the idea is the same. Use the values from the main from statement for your work rather than the values from some extra, unnecessary subquery.

group_concat missing on of the group

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`