Why find_in_set works but IN clause - mysql

I have code like this.
DELIMITER $$
CREATE PROCEDURE `sp_deleteOrderData`(orderid BIGINT(11))
BEGIN
DECLARE shipmentnumbers VARCHAR(1000);
DECLARE cartonid VARCHAR(1000);
SELECT GROUP_CONCAT(a_shipmentid) FROM t_shipment WHERE a_orderid = orderid INTO shipmentnumbers;
SELECT GROUP_CONCAT(a_cartonid) FROM t_carton WHERE a_shipmentid IN (shipmentnumbers) INTO cartonid;
SELECT shipmentnumbers;
/*SELECT cartonid; */
END$$
DELIMITER ;
Here shipmentnumbers returns 100020,100021,100022
Ideally cartonid should be returned as 11,12,13
But i get only 11 as cartonid.
But when i use below code i get proper result.
SELECT GROUP_CONCAT(a_cartonid) FROM t_carton WHERE FIND_IN_SET( a_shipmentid, shipmentnumbers ) INTO cartonid;
I wanted to know what exactly is the difference between IN and FIND_IN_SET and hwo di i decide what to use.

IN accepts a list or parameters to search, FIND_IN_SET accepts a string parameter containing a comma-separated list:
SELECT 1 IN (1, 2, 3, 4)
SELECT FIND_IN_SET(1, '1,2,3,4')
If you try to apply IN to a comma-separated string, it will treat it as a single parameter and will match it as a whole:
SELECT 1 IN ('1,2,3,4')
Of course, the string '1' is not equal to the string '1,2,3,4' so the query above returns false.

FIND_IN_SET searches a string inside a set of string separated by a comma.
MySQL FIND_IN_SET
But you don't need to use GROUP_CONCAT to concatenate the rows to be used in the IN clause, try this,
SELECT GROUP_CONCAT(a_cartonid)
FROM t_carton
WHERE a_shipmentid IN
(
SELECT a_shipmentid
FROM t_shipment
WHERE a_orderid = orderid
)
or use JOIN
SELECT GROUP_CONCAT(DISTINCT a.a_cartonid)
FROM t_carton a
INNER JOIN
(
SELECT a_shipmentid
FROM t_shipment
WHERE a_orderid = orderid
) b ON a.a_shipmentid = b.a_shipmentid

Related

mysql where columnname in (function(a value)) not working

I have a strange situation here with the mysql query:
When the WHERE unterkategorie IN (children_csv(1)) is used there is no result.
Second "WHERE unterkategorie IN (11,12,13,28,29,32,14,15,16,30,31,33,34,35)" is fetching records when I substitute function name with the results of the function when executed separately
the full query is:
SELECT k.name category_name,
p.unterkategorie,
p.artikelnummer,
p.hauptkategorie,
p.id,
p.name product_name,
p.preis,
p.sortierung,
p.verpackungseinheit
FROM produkte p, kategorie k
WHERE unterkategorie IN (children_csv(1))
WHERE unterkategorie IN (11,12,13,28,29,32,14,15,16,30,31,33,34,35)
AND p.unterkategorie = k.id
ORDER BY unterkategorie, p.sortierung
Following is the function definition
delimiter //
CREATE DEFINER=`root`#`localhost` FUNCTION `children_csv`(child int)RETURNS varchar(1000) CHARSET utf8
BEGIN
declare return_value varchar(1000);
SELECT GROUP_CONCAT(Level SEPARATOR ',')childrens into return_value FROM (
SELECT #Ids := (
SELECT GROUP_CONCAT(`id` SEPARATOR ',')
FROM `kategorie`
WHERE FIND_IN_SET(`parent`, #Ids)
ORDER BY parent, sortierung
) Level
FROM `kategorie`
JOIN (SELECT #Ids := child) temp1
WHERE FIND_IN_SET(`parent`, #Ids)
) temp2;
RETURN return_value;
END;
//
delimiter ;
Your function is returning a single value, a string. It is not returning a a list of values (because MySQL functions do not do that). If you want to use the function directly, you can use find_in_set():
WHERE find_in_set(unterkategorie, children_csv(1))
I will caution you that MySQL cannot use an index on unterkategorie, so this might be slower.
If you want a faster query, then you can construct a query as a string (called dynamic SQL) and use prepare and exec to run it.
If you are coming from another programming language, you need to learn that functions are not the route to better performance in SQL. Moving the logic into a function generally does not help performance.

Err 1292 - Truncated incorrect DOUBLE value:'70,40,77,28,23,72,82,38,29'

When I execute this function like this:
This is my function defined:
CREATE FUNCTION kyf_getFormationComat(heroSdIds VARCHAR(128), uid VARCHAR(128))
RETURNS INT(11)
BEGIN
DECLARE comat INT(11);
SET comat = (SELECT SUM(a.comat) FROM ky_heroinfo a WHERE a.uid = uid AND a.staticdata_id in (heroSdIds));
RETURN comat;
END
heroSdIds is a string, therefore you can't use IN() in your SELECT query:
SELECT SUM(a.comat) FROM ky_heroinfo a
WHERE a.uid = uid AND a.staticdata_id in (heroSdIds)
In condition:
a.staticdata_id in (heroSdIds)
actually gives you:
a.staticdata_id in ("70,40,77,28,23,72,82,38,29")
Please note the quotes. It's still a string, which is a single value here. But to make it work, IN() would require it to be multiple values separated with commas:
a.staticdata_id in (70,40,77,28,23,72,82,38,29)
To make it work you can try with string function FIND_IN_SET() function instead of IN().
Try with:
FIND_IN_SET(a.staticdata_id, heroSdIds)
So the whole query would be:
SELECT SUM(a.comat) FROM ky_heroinfo a
WHERE a.uid = uid AND FIND_IN_SET(a.staticdata_id, heroSdIds)

How to use GROUP_CONCAT in Stored procedure MySQL

I'm trying to use comma separated values (#job_skills) in another query like as follows in my stored procedure. But it does not work as expected. Amy be it only consider first value from comma separated string. Is there any other way to patch up ?
SELECT GROUP_CONCAT(skillId) as job_skills FROM tbljob_skill as jskl WHERE jskl.jobpostingId = param_jobid INTO #job_skills;
BLOCK6: BEGIN
DECLARE curl_job12 CURSOR FOR SELECT * FROM (SELECT js.jobseekerId FROM tbljobseeker_skill as jss INNER JOIN tbmstjobseeker as js ON (js.jobseekerId = jss.jobseekerId) INNER JOIN tblrecommended_jobseekers_details as rjd ON (js.jobseekerId=rjd.jobseekerId) WHERE jss.skillId IN (#job_skills) AND js.isActive = 'Y' AND js.lat<>0 AND js.lang<>0 AND rjd.sessionid=param_sessionid GROUP BY js.jobseekerId) as m;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET job_done12 = TRUE;
OPEN curl_job12;
read_loop_job12: LOOP
FETCH curl_job12 INTO jsid, miles;
IF job_done12 THEN
LEAVE read_loop_job12;
END IF;
INSERT INTO temp_skills (jobId,skill,jobseekerId) VALUES (param_sessionid,#job_skills,jsid);
IF jsid <> '' THEN
SELECT COUNT(1) INTO check_exists FROM tblrecommended_jobseekers_details WHERE sessionid = param_sessionid AND type = 'match 1 - SKILLS' AND jobseekerId = jsid;
IF check_exists = 0 THEN
START TRANSACTION;
INSERT INTO temp_jobseekers_scores (jobseekerId,score,type,miles) VALUES (jsid,score_skills,'match 1',miles);
INSERT INTO tblrecommended_jobseekers_details (jobseekerId,score,type,miles,sessionid) VALUES (jsid,score_skills,'match 1 - SKILLS',miles,param_sessionid);
COMMIT;
END IF;
END IF;
END LOOP;
CLOSE curl_job12;
END BLOCK6;
Generated comma separated values are a SET and treated as a single string when used with IN function. Instead you have to use FIND_IN_SET to compare with other values.
Change your WHERE clause in CURSOR definition as below.
DECLARE curl_job12 CURSOR FOR
SELECT * FROM (
SELECT js.jobseekerId
FROM tbljobseeker_skill as jss
INNER JOIN tbmstjobseeker as js
ON (js.jobseekerId = jss.jobseekerId)
INNER JOIN tblrecommended_jobseekers_details as rjd
ON (js.jobseekerId=rjd.jobseekerId)
-- WHERE jss.skillId IN (#job_skills)
WHERE FIND_IN_SET( jss.skillId, #job_skills )
AND js.isActive = 'Y'
AND js.lat<>0
AND js.lang<>0
AND rjd.sessionid=param_sessionid
GROUP BY js.jobseekerId
) as m;
Refer to Documentation:
FIND_IN_SET(str,strlist)
Returns a value in the range of 1 to N if the string str is in the
string list strlist consisting of N substrings

GROUP_CONCAT as input of MySQL function

Is it possible to use a GROUP_CONCAT in a SELECT as the input of a MySQL function? I cannot figure out how to cast the variable it seems. I've tried blob. I've tried text (then using another function to break it up into a result set, here) but I haven't had any success.
I want to use it like this:
SELECT
newCustomerCount(GROUP_CONCAT(DISTINCT items.invoicenumber)) AS new_customers
FROM items;
Here is the function:
DROP FUNCTION IF EXISTS newCustomerCount;
DELIMITER $$
CREATE FUNCTION newCustomerCount(invoicenumbers BLOB)
RETURNS INT
DETERMINISTIC
BEGIN
DECLARE new_customers INT;
SET new_customers = 0;
SELECT
SUM(nc.record) INTO new_customers
FROM (
SELECT
1 AS customer,
(SELECT COUNT(*) FROM person_to_invoice ps2 WHERE person_id = ps1.person_id AND invoice < ps1.invoice) AS previous_invoices
FROM person_to_invoice ps1
WHERE invoice IN(invoicenumbers)
HAVING previous_invoices = 0
) nc;
RETURN new_customers;
END$$
DELIMITER ;
Because Mysql functions do not support dynamic queries, I recommend you re-think your basic strategy to pass in a list of invoice numbers to your function. Instead, you could modify your function to accept a single invoice number and return the number of new customers just for the one invoice number.
Also, there are some optimizations you can make in your query for finding the number of new customers.
DROP FUNCTION IF EXISTS newCustomerCount;
DELIMITER $$
CREATE FUNCTION newCustomerCount(p_invoice INT)
RETURNS INT
DETERMINISTIC
BEGIN
DECLARE new_customers INT;
SET new_customers = 0;
SELECT
COUNT(DISTINCT ps1.person_id) INTO new_customers
FROM
person_to_invoice ps1
WHERE
ps1.invoice = p_invoice
AND NOT EXISTS (
SELECT 1
FROM person_to_invoice ps2
WHERE ps1.person_id = ps2.person_id
AND ps2.invoice < ps1.invoice
);
RETURN new_customers;
END$$
DELIMITER ;
Then you can still get the total number of new customers for a given list of invoice numbers like this:
SELECT
SUM(newCustomerCount(invoice)) as total_new_customers
FROM items
WHERE ...
You could try FIND_IN_SET() instead of IN(). The performance will probably be horrible when passing in a long list of invoice numbers. But it should work.
WHERE FIND_IN_SET(invoice, invoicenumbers)
You are looking in the wrong place.
WHERE invoice IN(invoicenumbers) will not do the desired substitution. Instead you need to use CONCAT to construct the SQL, then prepare and execute it.

How to use values returned from mysql stored function with in() function?

I have a field named 'dealBusinessLocations' (in a table 'dp_deals') which contain some ids of another table(dp_business_locations) in comma separated format.
dealBusinessLocations
----------------------
0,20,21,22,23,24,25,26
I need to use this values within an in() function of a query.
like
select * from dp_deals as d left join dp_business_locations as b on(b.businessLocID IN (d.dealBusinessLocations) ;
Sine mysql doesn't support any string explode function, I have created a stored function
delimiter //
DROP FUNCTION IF EXISTS BusinessList;
create function BusinessList(BusinessIds text) returns text deterministic
BEGIN
declare i int default 0;
declare TmpBid text;
declare result text default '';
set TmpBid = BusinessIds;
WHILE LENGTH(TmpBid) > 0 DO
SET i = LOCATE(',', TmpBid);
IF (i = 0)
THEN SET i = LENGTH(TmpBid) + 1;
END IF;
set result = CONCAT(result,CONCAT('\'',SUBSTRING(TmpBid, 1, i - 1),'\'\,'));
SET TmpBid = SUBSTRING(TmpBid, i + 1, LENGTH(TmpBid));
END WHILE;
IF(LENGTH(result) > 0)
THEN SET result = SUBSTRING(result,1,LENGTH(result)-1);
END IF;
return result;
END//
delimiter ;
The function is working perfectly.
mysql> BusinessList( '21,22' )
BusinessList( '21,22' )
-----------------------
'21','22'
But the query using the function does not worked either. here is the query.
select * from dp_deals as d left join dp_business_locations as b on(b.businessLocID IN (BusinessList(d.dealBusinessLocations)));
I have also tried using static value for function argumet, But no use
select * from dp_deals as d left join dp_business_locations as b on(b.businessLocID IN (BusinessList('21,22')));
It seems that there is some problem with using value returned from the function.
First, read this:
Is storing a comma separated list in a database column really that bad?
Yes, it is
Then, go and normalize your tables.
Now, if you really can't do otherwise, or until you normalize, use the FIND_IN_SET() function:
select *
from dp_deals as d
left join dp_business_locations as b
on FIND_IN_SET(b.businessLocID, d.dealBusinessLocations)
Then, read that article again. If the query is slow or if you have other problems with this table, then you'll know why:
Is storing a comma separated list in a database column really that bad?
Yes, it is
Simple, use find_in_set() instead.
SELECT *
FROM dp_deals as d
LEFT JOIN dp_business_locations as b
ON (FIND_IN_SET(b.businessLocID,d.dealBusinessLocations) > 0);
See: http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_find-in-set
Note that if you drop CSV and get out off hell you can use a simple join like:
SELECT d.*, GROUP_CONCAT(b.dealBusinessLocation) as locations
FROM dp_deals as d
LEFT JOIN dp_business_location as b
ON (d.dealBusinessLocation = b.businessLocID);
Which will be much much faster and normalized as a bonus.
I think your problem is that IN() doesn't expect to get one string with lots of fields in it, but lots of fields.
With your function you are sending it this:
WHERE something IN ('\'21\',\'22\''); /* i.e. a single text containing "'21','22'" */
And not the expected
WHERE something IN ('21','22');