SQL problem, LEFT JOIN [..] IN() - mysql

This small SQL error is bugging me. It doesn't seem to be a problem with the query, just the scope(?), examples work best:
SELECT ocp.*, oc.*, GROUP_CONCAT( u.username SEPARATOR ', ') AS `memjoined`
FROM gangs_ocs_process ocp, gangs_ocs oc
LEFT JOIN users u ON u.userid IN ( ocp.membersin )
WHERE ocp.ocid =1 AND ocp.gangid =1 AND oc.oc_name = ocp.crimename
GROUP BY ocp.ocid
LIMIT 0 , 30
Theres a column (gangs_ocs_process.membersin) which has a list of IDs that have joined (ie 1,2,5). I'm trying to get the usernames for each of these IDs (from the users table) in one go.
The problem is LEFT JOIN users u ON u.userid IN ( ocp.membersin )
If I substitue 1,2,4 in for ocp.membersin (putting the literal list instead of column name), it works ok. It returns a column that has the usernames (image). However, if I leave in the ocp.membersin, I get this error:
#1054 - Unknown column 'ocp.membersin' in 'on clause'
This is the first time I've even used IN in left joins so I'm a bit lost.
Any help would be great :)

I don't think that "IN" will work for this syntax. MySQL expects IN to be something akin to a dataset, not a delimited string. I think you need to find a way to take membersin, expand it into a dataset MySQL can work with (maybe a temporary table), and join on that.

If you have delimited strings in your table, you have a design problem in your database. Add a new table to hold these values.

Are you sure 'membersin' is in the 'gangs_ocs_process' table, and not the 'gangs_ocs' table?

The reason you can't get it to work is because first you need to get your database NORMALIZED. You should NEVER, EVER have a list of ID's in a single column.

After taking another look, I think your problem is trying to aggregate at the wrong point as well as the IN syntax and that you should aggregate in a subquery restricted by the contents of the IN. I don't know enough about your schema to make this out of the box correct, but you want something like this. SomeKeyfield should relate back to gangs_ocs_process
SELECT ocp.*, oc.*, u.Memjoined
FROM gangs_ocs_process ocp, gangs_ocs oc
LEFT JOIN (Select SomeKeyField, GROUP_CONCAT( u.username SEPARATOR ', ') as memjoined
from users where userid in
(select membersin from gangs_ocs_process
where [whatever conditions] )
Group By SomeKeyField) u on ocp.SomeKeyField = u.SomeKeyField
WHERE ocp.ocid =1 AND ocp.gangid =1 AND oc.oc_name = ocp.crimename
GROUP BY ocp.ocid
LIMIT 0 , 30

This is a bad way to keep membership.
But if you still need to live with it, you may try REGEXP matching to test for membership:
SELECT ocp.*, oc.*, GROUP_CONCAT( u.username SEPARATOR ', ') AS `memjoined`
FROM gangs_ocs_process ocp
LEFT JOIN users u ON (ocp.membersin RLIKE CONCAT('(^|,)[[:blank:]]?', userid, '[[:blank:]]?($|,)'))
JOIN gangs_ocs oc ON (ocp.ocid = 1 AND ocp.gangid = 1 AND oc.oc_name = ocp.crimename)
GROUP BY ocp.ocid
LIMIT 0 , 30

Related

MySQL DISTINCT returning not so distinct results

Good day,
I have a small issue with MySQL Distinct.
Trying the following query in my system :
SELECT DISTINCT `booking_id`, `booking_ticket`, `booking_price`, `bookingcomment_id`, `bookingcomment_message` FROM `mysystem_booking`
LEFT JOIN `mysystem_bookingcomment` ON `mysystem_booking`.`booking_id` = `mysystem_bookingcomment`.`bookingcomment_link`
WHERE `booking_id` = 29791
The point is that there are bookings like 29791 that have many comments added.
Let's say 10. Then when running the above query I see 10 results instead of one.
And that's not the way DISTINCT supposes to work.
I simply want to know if there are any comments. If the comment ID is not 0 then there is a comment. Of course I can add COUNT(blabla) as comment_number but that's a whole different story. For me now I'd like just to have this syntax right.
You may try aggregating here, to find which bookings have at least a single comment associated with them:
SELECT
b.booking_id,
b.booking_ticket,
b.booking_price
FROM mysystem_booking b
LEFT JOIN mysystem_bookingcomment bc
ON b.booking_id = bc.bookingcomment_link
WHERE
b.booking_id = 29791
GROUP BY
b.booking_id
HAVING
COUNT(bc.bookingcomment_link) > 0;
Note that depending on your MySQL server mode, you might have to also add the booking_ticket and booking_price columns to the GROUP BY clause to get the above query to run.
You can try below - using a case when expression
SELECT DISTINCT `booking_id`, `booking_ticket`, `booking_price`, `bookingcomment_id`,
case when `bookingcomment_message`<>'0' then 'No' else 'Yes' end as comments
FROM `mysystem_booking`
LEFT JOIN `mysystem_bookingcomment` ON `mysystem_booking`.`booking_id` = `mysystem_bookingcomment`.`bookingcomment_link`
WHERE `booking_id` = 29791

How do I add a NOT clause to my SQL query

I would like to add a NOT clause to this SQL query:
Select
members.Member_Id,
members.Title,
members.FirstName,
members.LastName,
members.Po_Box,
members.Street,
members.City,
members.Del,
members.Mobile,
members.eMail,
members.WFTD,
ship_info.Renewal_Date
From
members
Left Join
ship_info on (members.Member_Id = ship_info.Member_Id)
Order By
ship_info.Renewal_Date
The NOT clause is this:
Where Member_Id Not Between 2000 And 3000;
I have tried to place this line in different places, but get an error each time, and since Wamp reports errors in french, find it unhelpful.
Yes there are similar questions like this, but they confuse me more, as I don't understand them enough to modify my script accordingly. I don't know much about arrays and complex code.
I export data from the database for mail merge purposes, and Members whose Ids are in the 2000s are deleted Members, whose Id's were moved from their original Id to that of higher numbers, as I don't like deleting people permanently in case they change their mind later down the track.
Thank you.
You could try it like this:
Select members.Member_Id
,members.Title
,members.FirstName
,members.LastName
,members.Po_Box
,members.Street
,members.City
,members.Del
,members.Mobile
,members.eMail
,members.WFTD
,ship_info.Renewal_Date
From members
LEFT JOIN ship_info ON (members.Member_Id=ship_info.Member_Id)
WHERE (members.Member_ID < 2000 OR members.member_ID > 3000)
ORDER BY ship_info.Renewal_Date
Since you are using a JOIN and member_id field resides in both tables of the JOIN, you must specify for which table you need the where statement. Try:
SELECT members.Member_Id
,members.Title
,members.FirstName
,members.LastName
,members.Po_Box
,members.Street
,members.City
,members.Del
,members.Mobile
,members.eMail
,members.WFTD
,ship_info.Renewal_Date
FROM members
LEFT JOIN ship_info ON (members.Member_Id = ship_info.Member_Id)
WHERE members.Member_Id NOT BETWEEN 2000
AND 3000
ORDER BY ship_info.Renewal_Date
According to "Database Administration Fundamentals", the following would be the appropriate syntax:
Select members.Member_Id
,members.Title
,members.FirstName
,members.LastName
,members.Po_Box
,members.Street
,members.City
,members.Del
,members.Mobile
,members.eMail
,members.WFTD
,ship_info.Renewal_Date
From members
LEFT JOIN ship_info ON (members.Member_Id=ship_info.Member_Id)
WHERE NOT members.Member_Id BETWEEN 2000 AND 3000
ORDER BY ship_info.Renewal_Date ;
In the previously mentioned book, it states,
" JOIN statements can be specified in either the FROM or the WHERE
clause, but it is recommended that you specify them in the FROM clause
".
As for clause precedence, here is the correct order to follow:
SELECT -- "what" we want
FROM -- "where" to look for it
JOIN
WHERE -- condition
GROUP BY
HAVING
ORDER BY -- sorting order
I think below should work, if not then please specify error which you got, you just need to prefix table name members (better if you use this table name as alias)
SELECT members.Member_Id
,members.Title
,members.FirstName
,members.LastName
,members.Po_Box
,members.Street
,members.City
,members.Del
,members.Mobile
,members.eMail
,members.WFTD
,ship_info.Renewal_Date
FROM members
LEFT JOIN ship_info ON (members.Member_Id = ship_info.Member_Id)
WHERE members.Member_Id NOT BETWEEN 2000
AND 3000
ORDER BY ship_info.Renewal_Date

How to ORDER a LEFT JOIN for 2 tables

I've (on reflection ridiculously) stored our 'punters' in 2 tables depending on whether they registered or paid through the express checkout form.
My SQL looks like this:
SELECT
DISTINCT(sale_id), sale_punter_type, sale_comment, sale_refund, sale_timestamp,
punter_surname, punter_firstname,
punter_checkout_surname, punter_checkout_firstname,
punter_compo_surname, punter_compo_firstname,
sale_random, sale_scanned, sale_id
FROM
sale
LEFT JOIN
punter ON punter_id = sale_punter_no
LEFT JOIN
punter_checkout ON punter_checkout_id = sale_punter_no
LEFT JOIN
punter_compo ON punter_compo_id = sale_punter_no
WHERE
sale_event_no = :id
ORDER BY
punter_surname, punter_firstname,
punter_checkout_surname, punter_checkout_firstname
It returns results BUT lists the registered users alphabetically first, then the checkout punters alphabetically.
My question is there a way to get all of the users (registered or checkout) all together sorted alphabetically in 1 sorted list instead of 2 joined sorted lists.
I thought maybe I could use something like punter_checkout_surname AS punter_surname but that didn't work.
Any thoughts? I know now that I shouldn't have used 2 separate tables but but I'm stuck with it now.
Thank you.
I think you just want to use coalesce().
ORDER BY COALESCE(punter_surname, punter_checkout_surname)
COALESCE(punter_firstname, punter_checkout_firstname)
Other comments:
I doubt that DISTINCT is necessary. Why would this generate multiple rows for a single sale_id?
When a query has multiple tables, qualify all the column names (that is, include table aliases so you and others know where the table comes from).
Your data has three sets of names. That seems overkill.
You might want to put the COALESCE() in the SELECT so you don't have quite so many names generated by the query.
Here's my answer:
ORDER BY
COALESCE( UCASE( punter_surname) , UCASE( punter_checkout_surname ), UCASE( punter_compo_surname ) ) ,
COALESCE( UCASE( punter_firstname ) , UCASE( punter_checkout_firstname ), UCASE( punter_compo_firstname) )

mysql JOIN Inside a JOIN and search a group_concatenated values

I cant seem to find a solution for Searching a group_concatenated value,
I have 3 table that are connected with id's
1st table have the same value with 2nd table, but no same value with 3rd,
2nd table have the same value with 1st and 3rd table,
I want to get the value inside 3rd table,
concat the values in accordance to Distinct ID's of 2nd table, display them, and be able to search
this are my tables look like
how do i search for the concatenated values
please if there's a better way, your help is much appreciated?
the query below is what i have so far
$query = $db->prepare("
SELECT
a.problem_encountered,
GROUP_CONCAT(
DISTINCT
c.full_name)
AS
fnames
FROM
maintenance_sheet_table a
LEFT JOIN
mis_incharge_table b
ON
b.mis_incharge_id = a.mis_incharge_id
INNER JOIN
users_table c
ON
c.mis_id=b.mis_id
WHERE
a.problem_encountered
LIKE
:findMe
HAVING
fnames
LIKE
:findMe
GROUP BY a.id ORDER BY a.id
");
$query->bindValue(':findMe', '%' . $keywordSearch. '%');
A potential answer is to filter the Users_table in a subquery. There are a number of different forms of this option, and hard to tell from your data which is required. The one I have below simply returns the users that match the search criteria.
SELECT a.problem_encountered, GROUP_CONCAT(DISTINCT innerc.full_name) AS fnames
FROM maintenance_sheet_table a
LEFT JOIN mis_incharge_table b ON b.mis_incharge_id = a.mis_incharge_id
LEFT JOIN (SELECT c.mis_id, c.full_name
FROM users_table c
WHERE c.full_name LIKE :findMe) innerc ON innerc.mis_id=b.mis_id
WHERE a.problem_encountered LIKE :findMe
GROUP BY a.id
ORDER BY a.id
However, you could also do the concatenation within the subquery if required.
SELECT a.problem_encountered, innerc.fnames
FROM maintenance_sheet_table a
INNER JOIN (SELECT mit.mis_incharge_id, GROUP_CONCAT(DISTINCT ut.full_name) AS fnames
FROM users_table ut
INNER JOIN mis_incharge_table mit ON ut.user_id = mit.user_id
GROUP BY mit.mis_incharge_id
HAVING fnames LIKE :findMe) innerc ON innerc.mis_incharge_id = a.mis_incharge_id
WHERE a.problem_encountered LIKE :findMe
GROUP BY a.id
ORDER BY a.id
Note: I agree with spencer7593, that you shouldn't use the same :findMe variable against 2 separate fields. Even if it works, to a maintenance programmer or even yourself in a few years time, will probably look at this and think that the wrong fields are being interrogated.
You can "search" the return from the GROUP_CONCAT() expression in the HAVING clause. As a more efficient alternative, I suspect you could use an EXISTS predicate with a subquery.
I suspect part of the problem is that your query is referencing the same bind placeholder more than one time. (In previous releases of PDO, this was a restriction, a named bind placeholder could be referenced only once.)
The workaround to this issue is to use a separate bind placeholder, e.g.
HAVING fnames LIKE :findMeToo
And then bind a value to each placeholder:
$query->bindValue(':findMe', '%' . $keywordSearch. '%');
$query->bindValue(':findMeToo', '%' . $keywordSearch. '%');
(With this issue, I don't think PDO issued a warning or error; the effect was as if no value was supplied for the second reference to the named bind placeholder. Not sure if this issue is fixed, either by a code change or a documentation update. The workaround as above, reference a bind placeholder only once within a query.)
Beyond that, it's not clear what problem you are observing.
Your HAVING clause should come after your GROUP BY clause
change
HAVING
fnames
LIKE
:findMe
GROUP BY a.id ORDER BY a.id
to
GROUP BY a.id
HAVING
fnames
LIKE
:findMe
ORDER BY a.id

Correlated Subquery in a MySQL CASE Statement

Here is a brief explanation of what I'm trying to accomplish; my query follows below.
There are 4 tables and 1 view which are relevant for this particular query (sorry the names look messy, but they follow a strict convention that would make sense if you saw the full list):
Performances may have many Performers, and those associations are stored in PPerformer. Fans can have favorites, which are stored in Favorite_Performer. The _UpcomingPerformances view contains all the information needed to display a user-friendly list of upcoming performances.
My goal is to select all the data from _UpcomingPerformances, then include one additional column that specifies whether the given Performance has a Performer which the Fan added as their favorite. This involves selecting the list of Performers associated with the Performance, and also the list of Performers who are in Favorite_Performer for that Fan, and intersecting the two arrays to determine if anything is in common.
When I execute the below query, I get the error #1054 - Unknown column 'up.pID' in 'where clause'. I suspect it's somehow related to a misuse of Correlated Subqueries but as far as I can tell what I'm doing should work. It works when I replace up.pID (in the WHERE clause of t2) with a hard-coded number, and yes, pID is an existing column of _UpcomingPerformances.
Thanks for any help you can provide.
SELECT
up.*,
CASE
WHEN EXISTS (
SELECT * FROM (
SELECT RID FROM Favorite_Performer
WHERE FanID = 107
) t1
INNER JOIN
(
SELECT r.ID as RID
FROM PPerformer pr
JOIN Performer r ON r.ID = pr.Performer_ID
WHERE pr.Performance_ID = up.pID
) t2
ON t1.RID = t2.RID
)
THEN "yes"
ELSE "no"
END as pText
FROM
_UpcomingPerformances up
The problem is scope related. The nested Selects make the up table invisible inside the internal select. Try this:
SELECT
up.*,
CASE
WHEN EXISTS (
SELECT *
FROM Favorite_Performer fp
JOIN Performer r ON fp.RID = r.ID
JOIN PPerformer pr ON r.ID = pr.Performer_ID
WHERE fp.FanID = 107
AND pr.Performance_ID = up.pID
)
THEN 'yes'
ELSE 'no'
END as pText
FROM
_UpcomingPerformances up