Mysql return more than one row - mysql

I have this query.
SELECT notes.id,enter.name as 'enter_name',step.title as 'flow status',notes.user_name as user_created,notes.created,notes.rel_client_id,td_doc_nr.value_string as 'document number',enter.enter_code,
IF(!ISNULL(td_doc_nr.value_string),
(SELECT GROUP_CONCAT(product_name SEPARATOR ',') from notes d
join note_bundles b on b.note_id = d.id
join note_products p on p.doc_bundle_id = b.id
join note_product_get_fields f on f.doc_product_id = p.id
join note_product_get_field_data fd on fd.get_field_id = f.id
where d.doc_nr = td_doc_nr.value_string
and value_string ='auto')
,NULL) as test
FROM notes notes
JOIN notes_steps step ON step.id = notes.step_id
JOIN notes_enters enter ON enter.id = notes.enter_id
LEFT JOIN notes_custom_fields tf_doc_nr ON tf_doc_nr.name = 'note_number' AND tf_doc_nr.rel_entity_id = enter.id
LEFT JOIN notes_custom_field_data td_doc_nr ON td_doc_nr.rel_entity_id = notes.id AND
td_doc_nr.field_instance_id = tf_doc_nr.id
WHERE notes.enter_id in (777) AND notes.status = 1
I added this subquery to the 'if statement'
SELECT GROUP_CONCAT(product_name SEPARATOR ',') from nontes d
join note_bundles b on b.note_id = d.id
join note_products p on p.doc_bundle_id = b.id
join note_product_get_fields f on f.doc_product_id = p.id
join note_product_get_field_data fd on fd.get_field_id = f.id
where d.doc_nr = 'G7777777'
and value_string ='auto'
After this I added a new column.
SELECT GROUP_CONCAT(product_name SEPARATOR ','),GROUP_CONCAT(DISTINCT b.msisdn SEPARATOR ',') from notes d
join note_bundles b on b.note_id = d.id
join note_products p on p.doc_bundle_id = b.id
join note_product_get_fields f on f.doc_product_id = p.id
join note_product_get_field_data fd on fd.get_field_id = f.id
where d.doc_nr = 'G7777777'
and value_string ='auto'
It returns two columns.
How can I return two columns?Is it possible? :) Thanks

A subquery inside an IF statement can't return multiple columns. You will need to join the subquery into the results, and pull out the two separate columns individually:
SELECT ...
IF(!ISNULL(td_doc_nr.value_string), sub.one, NULL) as one,
IF(!ISNULL(td_doc_nr.value_string), sub.two, NULL) as two
FROM ...
LEFT JOIN (
SELECT d.doc_nr, GROUP_CONCAT(product_name SEPARATOR ','),GROUP_CONCAT(DISTINCT b.msisdn SEPARATOR ',') from documents d
join document_bundles b on b.document_id = d.id
join document_products p on p.doc_bundle_id = b.id
join document_product_cstm_fields f on f.doc_product_id = p.id
join document_product_cstm_field_data fd on fd.cstm_field_id = f.id
where value_string ='auto'
group by d.doc_nr
) sub on sub.doc_nr = td_doc_nr.value_string

A correlated subquery inside an IF statement can only return 1 column and 1 row, this is why you are getting the error. However, looking over your query the only outer reference inside the subquery is
d.doc_nr = td_doc_nr.value_string
So you do not need actually need a correlated subquery and you can achieve the same result by moving the subquery to a join and grouping by doc_nr within the subquery, which will probably be much more efficient, and it will allow you to return the 2 columns you want:
SELECT tickets.id,
source.name as 'source_name',
flow_stage.title as 'flow status',
tickets.user_name as user_created,
tickets.created,
tickets.rel_client_id,
td_doc_nr.value_string as 'document number',
source.source_code,
IF(!ISNULL(td_doc_nr.value_string), ProductNames, NULL) as test,
d.MSISDNS
FROM tickets tickets
JOIN tickets_flow_stages flow_stage
ON flow_stage.id = tickets.flow_stage_id
JOIN tickets_sources source
ON source.id = tickets.source_id
LEFT JOIN tickets_custom_fields tf_doc_nr
ON tf_doc_nr.name = 'document_number'
AND tf_doc_nr.rel_entity_id = source.id
LEFT JOIN tickets_custom_field_data td_doc_nr
ON td_doc_nr.rel_entity_id = tickets.id
AND td_doc_nr.field_instance_id = tf_doc_nr.id
LEFT JOIN
( SELECT d.Doc_nr,
GROUP_CONCAT(product_name SEPARATOR ',') AS ProductNames,
GROUP_CONCAT(DISTINCT b.msisdn SEPARATOR ',') AS MSISDNS
from documents d
INNER JOIN document_bundles b
ON b.document_id = d.id
INNER JOIN document_products p
ON p.doc_bundle_id = b.id
INNER JOIN document_product_cstm_fields f
ON f.doc_product_id = p.id
INNER JOIN document_product_cstm_field_data fd
ON fd.cstm_field_id = f.id
WHERE value_string ='auto'
GROUP BY d.Doc_nr
) d
ON d.doc_nr = td_doc_nr.value_string
WHERE tickets.source_id IN (114,122,125,129,131)
AND tickets.status = 1

Related

FIND_IN_SET is getting wrong results

First, I searched about the same problem but I didn't find an appropriate solution.
My problem is with the following code, it's returning wrong results:
SELECT FbID,FhID,
FbRef,
FbDate,
(D.AccName) AS DName,
FbQuan,
CONCAT(CategoryName,'-',ProductName) AS ProdName,
(C.AccName) AS CusName,
FhPurPrice,FbSalePrice,
(R.AccName) AS ResoName,
Curr1.CurrencyName,
Curr2.CurrencyName,
Plc1.PlaceName AS FhResoPlaceName,
Plc2.PlaceName AS FbCusPlaceName,
'linked' AS xLinkStatus,
1 AS xStatus
FROM tblfatora2 F2
INNER JOIN tblfatora1 F1 ON F1.FhRef = F2.FhRef
INNER JOIN tblproducts P ON P.ProductID = F1.FhProduct
INNER JOIN tblcategories CT ON CT.CategoryID = P.ProductCategory
INNER JOIN tblaccounts R ON R.AccID = F1.FhReso
INNER JOIN tblaccounts C ON C.AccID = F2.FbCus
INNER JOIN tblaccounts D ON D.AccID = F1.FhDriver
INNER JOIN tblcurrencies Curr1 ON C.AccCurrID = Curr1.CurrencyID
INNER JOIN tblcurrencies Curr2 ON R.AccCurrID = Curr2.CurrencyID
LEFT JOIN tblplaces Plc1 ON F1.FhResoPlace = Plc1.PlaceID
LEFT JOIN tblplaces Plc2 ON F2.FbCusPlace = Plc2.PlaceID
WHERE FIND_IN_SET(`FhID`, '18313,18314')
ORDER BY FbDate, FbID
the results that it gives me are: enter image description here
note: I use FIND_IN_SET here because I can't use (IN) where I use that SQL statement in a procedure inside vb.net code:
Public Sub MySql_GetLinked()
xDtAll = New DataTable()
Dim xPar(0) As MySqlParameter
xPar(0) = New MySqlParameter("#FhID", MySqlDbType.String) With {
.Value = LinkedFatora}
xClsMySql.GetData(xSqlLinked, xDtAll, xPar)
End Sub
So I fill the variable (LinkedFatora) by loop and I use the same SQL statement but I replace (WHERE FIND_IN_SET('FhID', '18313,18314')) with (WHERE FIND_IN_SET('FhID', #FhID)).
I looked for the error's reason but couldn't catch it.
I found the problem, it's changing (WHERE FIND_IN_SET(FhID, '18313,18314')) to (WHERE FIND_IN_SET(FbID, '18313,18314')):
SELECT FbID,FhID,
FbRef,
FbDate,
(D.AccName) AS DName,
FbQuan,
CONCAT(CategoryName,'-',ProductName) AS ProdName,
(C.AccName) AS CusName,
FhPurPrice,FbSalePrice,
(R.AccName) AS ResoName,
Curr1.CurrencyName,
Curr2.CurrencyName,
Plc1.PlaceName AS FhResoPlaceName,
Plc2.PlaceName AS FbCusPlaceName,
'linked' AS xLinkStatus,
1 AS xStatus
FROM tblfatora2 F2
INNER JOIN tblfatora1 F1 ON F1.FhRef = F2.FhRef
INNER JOIN tblproducts P ON P.ProductID = F1.FhProduct
INNER JOIN tblcategories CT ON CT.CategoryID = P.ProductCategory
INNER JOIN tblaccounts R ON R.AccID = F1.FhReso
INNER JOIN tblaccounts C ON C.AccID = F2.FbCus
INNER JOIN tblaccounts D ON D.AccID = F1.FhDriver
INNER JOIN tblcurrencies Curr1 ON C.AccCurrID = Curr1.CurrencyID
INNER JOIN tblcurrencies Curr2 ON R.AccCurrID = Curr2.CurrencyID
LEFT JOIN tblplaces Plc1 ON F1.FhResoPlace = Plc1.PlaceID
LEFT JOIN tblplaces Plc2 ON F2.FbCusPlace = Plc2.PlaceID
WHERE FIND_IN_SET(`FbID`, '18313,18314')
ORDER BY FbDate, FbID

How to optimize join query with sub query

How can I optimize this join query? how can I get the latest start_dt in this query? Is any way can make it faster to load and the start_dt is the latest one? If I delete the subquery for this case, I will get the record which the start_dt is not the latest one. Please Help!!
SELECT
matrix.matrix_uuid,
matrix.name AS matrix_name,
courses.courses_uuid,
courses.name AS courses_name,
employees.employees_uuid,
CONCAT(employees.first_name,
' ',
employees.last_name) AS Name,
courses.validity,
courses.duration,
(SELECT
MAX(start_dt)
FROM
courses_schedule C
WHERE
C.courses_schedule_uuid = CS.courses_schedule_uuid) AS start_dt,
COUNT(courses.courses_uuid) AS refresher,
courses.courses_refresher_uuid,
courses_taken.overall_status,
courses_taken.status
FROM
employees
INNER JOIN
courses_taken ON courses_taken.employees_uuid = employees.employees_uuid
LEFT JOIN
courses_schedule CS ON CS.courses_schedule_uuid = courses_taken.courses_schedule_uuid
LEFT JOIN
matrix_courses matrix_courses ON matrix_courses.courses_uuid = CS.courses_uuid
LEFT JOIN
matrix ON matrix.matrix_uuid = matrix_courses.matrix_uuid
LEFT JOIN
courses ON courses.courses_uuid = matrix_courses.courses_uuid
WHERE
CS.start_dt = (SELECT
MAX(start_dt)
FROM
courses_taken CT
LEFT JOIN
courses_schedule ON courses_schedule.courses_schedule_uuid = CT.courses_schedule_uuid
LEFT JOIN
matrix_courses matrix_courses ON matrix_courses.courses_uuid = courses_schedule.courses_uuid
LEFT JOIN
roles_courses ON roles_courses.courses_uuid = matrix_courses.courses_uuid
LEFT JOIN
matrix M ON M.matrix_uuid = matrix_courses.matrix_uuid
LEFT JOIN
courses C ON C.courses_uuid = matrix_courses.courses_uuid
WHERE
C.courses_uuid = courses.courses_uuid
AND M.matrix_uuid = matrix.matrix_uuid
GROUP BY matrix.matrix_uuid , courses.courses_uuid)
AND courses_taken.overall_status = 1
AND employees.status = 2
AND IFNULL(employees.STATUS, 1) != 0
GROUP BY matrix.matrix_uuid , courses.courses_uuid , employees.employees_uuid

mysql where not in to left outer join

I have the following query and would like to convert it to using a left outer join instead of a not in to see if it would run faster that way. It's currently taking this query about 40 seconds to run on our database. I'm not familiar enough with using outer joins for this type of thing to convert it myself.
select
c.contact_id as contact_id,
c.orgid as organization_id,
c.first_name as first_name,
c.last_name as last_name,
a.address_state as state
from cnx_contact as c
inner join cnx_address as a on c.home_address_uid = a.address_uid
where a.address_state = 'OH'
and (c.orgid = 45 or c.orgid = 55)
and c.contact_id NOT IN (
select pc.contact_id
from cnx_contact as c
inner join cnx_address as a on c.home_address_uid = a.address_uid
inner join cnx_contact_group_participant as gp on c.contact_id = gp.contact_id
inner join cnx_contact_participant_role as cr on gp.participant_role_uid = cr.participant_role_uid
inner join cnx_contact_group as cg on gp.group_uid = cg.group_uid
inner join cnx_contact_group_participant as pgp on cg.primary_participant_uid = pgp.participant_uid
inner join cnx_contact as pc on pgp.contact_id = pc.contact_id
where (c.orgid = 45 or c.orgid = 55)
and cr.name = 'Applicant'
);
select
c.columns
from cnx_contact as c
inner join cnx_address as a on c.home_address_uid = a.address_uid
LEFT JOIN
(Subquery goes here) x
ON x.contact _id = c.contact_id
where a.participant_state = 'OH'
and c.orgid IN(45,55)
and x.contact_id IS NULL;

specifying preferred results for GROUP BY values

I have the following (simplified) query:
SELECT
u.idnumber AS user_idnumber,
sst_status.value AS scorm_status,
sst_starttime.value AS scorm_starttime,
sst_duration.value AS scorm_duration,
sst_score.value AS scorm_score,
u.id as uid,
s.id as scormid,
ss.id as ssid
FROM {user} u
LEFT JOIN {prog_user_assignment} prua ON prua.userid = u.id
LEFT JOIN {prog} pr ON prua.programid = pr.id
LEFT JOIN {prog_courseset} prcs ON prcs.programid = pr.id
LEFT JOIN {prog_courseset_course} prcsc ON prcsc.coursesetid = prcs.id
LEFT JOIN {course} c ON prcsc.courseid = c.id
LEFT JOIN {scorm} s ON s.id = cm.instance
LEFT JOIN {scorm_scoes} ss ON ss.scorm = s.id
LEFT JOIN {scorm_scoes_track} sst_status
ON sst_status.userid = u.id
AND sst_status.scormid = ss.scorm
AND sst_status.element = 'cmi_core.lesson_status'
AND sst_status.scoid = ss.id
LEFT JOIN {scorm_scoes_track} sst_starttime
ON sst_starttime.userid = u.id
AND sst_starttime.scormid = ss.scorm
AND sst_starttime.element = 'x.start.time'
AND sst_starttime.scoid = ss.id
LEFT JOIN {scorm_scoes_track} sst_duration
ON sst_duration.userid = u.id
AND sst_duration.scormid = ss.scorm
AND sst_duration.element = 'cmi.core.total_time'
AND sst_duration.scoid = ss.id
LEFT JOIN {scorm_scoes_track} sst_score
ON sst_score.userid = u.id
AND sst_score.scormid = ss.scorm
AND sst_score.element = 'cmi.core.score.raw'
AND sst_score.scoid = ss.id
WHERE u.id IN([SOME_EXAMPLE_IDS_HERE]) AND u.deleted = 0
group by u.idnumber, c.id
Now the problem is that without the GROUP BY clause, some of the records have values for scorm_status, scorm_starttime etc. However, these contain duplicates. But, with the group by statement, only NULL values are returned for these columns. Does anyone have a clue on how to resolve this?
Using GROUP_CONCAT and SUBSTRING_INDEX:-
SELECT
u.idnumber AS user_idnumber,
SUBSTRING_INDEX(GROUP_CONCAT(sst_status.value), ',', 1) AS scorm_status,
SUBSTRING_INDEX(GROUP_CONCAT(sst_starttime.value), ',', 1) AS scorm_starttime,
SUBSTRING_INDEX(GROUP_CONCAT(sst_duration.value), ',', 1) AS scorm_duration,
SUBSTRING_INDEX(GROUP_CONCAT(sst_score.value), ',', 1) AS scorm_score,
u.id as uid,
s.id as scormid,
ss.id as ssid
FROM {user} u
LEFT JOIN {prog_user_assignment} prua ON prua.userid = u.id
LEFT JOIN {prog} pr ON prua.programid = pr.id
LEFT JOIN {prog_courseset} prcs ON prcs.programid = pr.id
LEFT JOIN {prog_courseset_course} prcsc ON prcsc.coursesetid = prcs.id
LEFT JOIN {course} c ON prcsc.courseid = c.id
LEFT JOIN {scorm} s ON s.id = cm.instance
LEFT JOIN {scorm_scoes} ss ON ss.scorm = s.id
LEFT JOIN {scorm_scoes_track} sst_status
ON sst_status.userid = u.id
AND sst_status.scormid = ss.scorm
AND sst_status.element = 'cmi_core.lesson_status'
AND sst_status.scoid = ss.id
LEFT JOIN {scorm_scoes_track} sst_starttime
ON sst_starttime.userid = u.id
AND sst_starttime.scormid = ss.scorm
AND sst_starttime.element = 'x.start.time'
AND sst_starttime.scoid = ss.id
LEFT JOIN {scorm_scoes_track} sst_duration
ON sst_duration.userid = u.id
AND sst_duration.scormid = ss.scorm
AND sst_duration.element = 'cmi.core.total_time'
AND sst_duration.scoid = ss.id
LEFT JOIN {scorm_scoes_track} sst_score
ON sst_score.userid = u.id
AND sst_score.scormid = ss.scorm
AND sst_score.element = 'cmi.core.score.raw'
AND sst_score.scoid = ss.id
WHERE u.id IN([SOME_EXAMPLE_IDS_HERE]) AND u.deleted = 0
group by u.idnumber, c.id
This is getting the first non null values of these fields. Note that this is relying on the fields not containing commas (but easy enough to specify a different separator for the GROUP_CONCAT). You can specify an order for the GROUP_CONCAT to get your preferred value if required.
Here is a very simplified version of your query:
select u.idnumber AS user_idnumber, sst_status.value AS scorm_status,
sst_starttime.value AS scorm_starttime, sst_duration.value AS scorm_duration,
sst_score.value AS scorm_score, u.id as uid, s.id as scormid, ss.id as ssid
. . .
group by u.idnumber, c.id;
You will notice that most of the columns in the select are not in the group by. This is using a (mis)feature of MySQL and would not generally be allowed in other databases.
What MySQL does is it chooses an abritrary value from one of the rows for the result. The idea is that this works nicely if the column is constant for the group. But, it doesn't usually work as expected in other circumstances.
If you want a non-NULL value, then you can use max() or min() to get the biggest or smallest value.
For instance:
min(sst_status.value) AS scorm_status
and so on.

MySql query: sometimes getting an empty result set

I have this query:
SELECT CONCAT(f.name, ' ', f.parent_names) AS FullName,
stts.name AS 'Status',
u.name AS Unit,
city.name AS City,
hus.mobile1 AS HusbandPhone,
wife.mobile1 AS WifePhone,
f.phone AS HomePhone,
f.contact_initiation_date AS InitDate,
fh.created_at AS StatusChangeDate,
cmt.created_at AS CommentDate,
cmt.comment AS LastComment,
f.reconnection_date AS ReconnectionDate,
(
SELECT GROUP_CONCAT(t.name)
FROM taggings tgs JOIN tags t
ON tgs.tag_id = t.id
WHERE tgs.taggable_type = 'family' AND
tgs.taggable_id = f.id
) AS HandlingStatus
FROM families f
JOIN categories stts ON f.family_status_cat_id = stts.id
JOIN units u ON f.unit_id = u.id
JOIN categories city ON f.main_city_cat_id = city.id
JOIN contacts hus ON f.husband_id = hus.id
JOIN contacts wife ON f.wife_id = wife.id
JOIN comments cmt ON f.id = cmt.commentable_id AND
cmt.created_at = (SELECT MAX(created_at) FROM comments WHERE commentable_id = f.id)
JOIN family_histories fh ON f.id = fh.family_id AND
fh.created_at = (SELECT MAX(created_at) FROM family_histories WHERE family_id = f.id AND family_history_cat_id = 1422) AND
fh.family_history_cat_id = 1422
WHERE f.id = 12212
The problem is with the second SELECT (the column - HandlingStatus).
I don't understand but when the column has results (tested as a stand alone query) - I get an empty result set, and when there are no results - I get a result.
Why?