MySql SP is not working - mysql

I am new in mysql I am trying to get data using below SP where I used if else statements
CREATE DEFINER=`Travel_user`#`%` PROCEDURE `new_procedure`(in bus_id int, in travel_id int, in tr_date Date, in Seat_no varchar(45))
BEGIN
select #SeatNo := Fare from Travel_booking.SpecialFares where TravelId=travel_id and date=tr_date ;
IF (#SeatNo = NULL) then
select #SeatNo := sd.Fare from SeatDetails as sd
inner join tbl_ColumnDetails as c on c.ColumnId=sd.ColId
inner join tbl_RowsDetails as r on r.RowId=c.RowID
inner join BusStructure as bs on bs.StructId=r.StructureId
inner join BusDetails as bd on bd.Structure=bs.StructId
where sd.IsActive=1 and bd.BusiId=bus_id and sd.travelid=travel_id and sd.SeatNo=Seat_no;
END IF;
END
I am calling it like
call Travel_booking.new_procedure(34, 53, '2018-03-01', 'L2');
First select query is working fine and its showing proper result.
But if statement giving blank result when the result of first query is blank.
I have check both query separately like below they both working.
First query,
select Fare from Travel_booking.SpecialFares
where TravelId=53 and date='2018-03-02';
Second query,
select sd.Fare from SeatDetails as sd
inner join tbl_ColumnDetails as c on c.ColumnId=sd.ColId
inner join BusStructure as bs on bs.StructId=r.StructureId
inner join BusDetails as bd on bd.Structure=bs.StructId
where sd.IsActive=1 and bd.BusiId=34 and sd.travelid=53 and sd.SeatNo='L2';
But when I execute them in SP and passing parameters to them so I am getting blank result in if statement in same query.

Thanks to everyone for responding and helping me to find out where i am wrong.
I got my answer
Just simply declaring null variable at the start of SP like
BEGIN
select #SeatNo := NULL;
select #SeatNo := Fare from Travel_booking.SpecialFares where TravelId=travel_id and date=tr_date ;....

My Suggestions would be.
1) You have many joins please take a look if each join has any values and if these joins are correctly connected, meaning the joins are of equal value.
2) Check your parameters, In you SQL environment, check if your parameters produces results or not.
3)A tip would also be, check if your parameters is being passed correctly. Creating a output would instantly tell you, the developer of what is wrong with your code.

IF (#SeatNo is NULL) then
select #SeatNo := sd.Fare from SeatDetails as sd
inner join tbl_ColumnDetails as c on c.ColumnId=sd.ColId
inner join tbl_RowsDetails as r on r.RowId=c.RowID
inner join BusStructure as bs on bs.StructId=r.StructureId
inner join BusDetails as bd on bd.Structure=bs.StructId
where sd.IsActive=1 and bd.BusiId=bus_id and sd.travelid=travel_id and sd.SeatNo=Seat_no;
END IF;
write is instead of '=';

Related

MySQL LEFT JOIN with WHERE function-call produces wrong result

From MySQL 5.7 I am executing a LEFT JOIN, and the WHERE clause calls a user-defined function of mine. It fails to find a matching row which it should find.
[Originally I simplified my actual code a bit for the purpose of this post. However in view of a user's proposed response, I post the actual code as it may be relevant.]
My user function is:
CREATE FUNCTION `jfn_rent_valid_email`(
rent_mail_to varchar(1),
agent_email varchar(45),
contact_email varchar(60)
)
RETURNS varchar(60)
BEGIN
IF rent_mail_to = 'A' AND agent_email LIKE '%#%' THEN
RETURN agent_email;
ELSEIF contact_email LIKE '%#%' THEN
RETURN contact_email;
ELSE
RETURN NULL;
END IF
END
My query is:
SELECT r.RentCode, r.MailTo, a.AgentEmail, co.Email,
jfn_rent_valid_email(r.MailTo, a.AgentEmail, co.Email)
AS ValidEmail
FROM rents r
LEFT JOIN contacts co ON r.RentCode = co.RentCode -- this produces one match
LEFT JOIN link l ON r.RentCode = l.RentCode -- there will be no match in `link` on this
LEFT JOIN agents a ON l.AgentCode = a.AgentCode -- there will be no match in `agents` on this
WHERE r.RentCode = 'ZAKC17' -- this produces one match
AND (jfn_rent_valid_email(r.MailTo, a.AgentEmail, co.Email) IS NOT NULL)
This produces no rows.
However. When a.AgentEmail IS NULL if I only change from
AND (jfn_rent_valid_email(r.MailTo, a.AgentEmail, co.Email) IS NOT NULL)
to
AND (jfn_rent_valid_email(r.MailTo, NULL, co.Email) IS NOT NULL)
it does correctly produce a matching row:
RentCode, MailTo, AgentEmail, Email, ValidEmail
ZAKC17, N, <NULL>, name#email, name#email
So, when a.AgentEmail is NULL (from non-matching LEFT JOINed row), why in the world does passing it to the function as a.AgentEmail act differently from passing it as a literal NULL?
[BTW: I believe I have used this kind of construct under MS SQL server in the past and it has worked as I would expect. Also, I can reverse the test of AND (jfn_rent_valid_email(r.MailTo, a.AgentEmail, co.Email) IS NOT NULL) to AND (jfn_rent_valid_email(r.MailTo, a.AgentEmail, co.Email) IS NULL) yet I still get no match. It's as though any reference to a.... as a parameter to the function causes no matching row...]
Most likely this is an issue with optimizer turning the LEFT JOIN into a INNER JOIN. The optimizer may do this when it believes that the WHERE-condition is always false for the generated NULL row (which it in this case is not).
You can take a look at the query plan with the EXPLAIN command, you will likely see different table order depending on the query variation.
If the actual logic of the function is to check all emails with one function call, you may have better luck with using a function that takes just one email address as parameter and use that for each email-column.
You can try without the function:
SELECT r.RentCode, r.MailTo, a.AgentEmail, co.Email,
jfn_rent_valid_email(r.MailTo, a.AgentEmail, co.Email)
AS ValidEmail
FROM rents r
LEFT JOIN contacts co ON r.RentCode = co.RentCode -- this produces one match
LEFT JOIN link l ON r.RentCode = l.RentCode -- there will be no match in `link` on this
LEFT JOIN agents a ON l.AgentCode = a.AgentCode -- there will be no match in `agents` on this
WHERE r.RentCode = 'ZAKC17' -- this produces one match
AND ((r.MailTo='A' AND a.AgentEmail LIKE '%#%') OR co.Email LIKE '%#%' )
Or wrap the function in a subquery:
SELECT q.RentCode, q.MailTo, q.AgentEmail, q.Email, q.ValidEmail
FROM (
SELECT r.RentCode, r.MailTo, a.AgentEmail, co.Email,
jfn_rent_valid_email(r.MailTo, a.AgentEmail, co.Email) AS ValidEmail
FROM rents r
LEFT JOIN contacts co ON r.RentCode = co.RentCode -- this produces one match
LEFT JOIN link l ON r.RentCode = l.RentCode -- there will be no match in `link` on this
LEFT JOIN agents a ON l.AgentCode = a.AgentCode -- there will be no match in `agents` on this
WHERE r.RentCode = 'ZAKC17' -- this produces one match
) as q
WHERE q.ValidEmail IS NOT NULL
Changing the call to the function in the WHERE clause to read
jfn_rent_valid_email(r.MailTo, IFNULL(a.AgentEmail, NULL), IFNULL(co.Email, NULL)) IS NOT NULL
solves the issue.
It appears that the optimizer feels it can incorrectly guess that the function will return NULL in the non-match LEFT JOIN case if a plain reference to a.AgentEmail is passed as any parameter. But if the column reference is inside any kind of expression the optimizer ducks out. Wrapping it inside a "dummy", seemingly pointless IFNULL(column, NULL) is thus enough to restore correct behaviour.
I am marking this as the accepted solution because it is by far the simplest workaround, requiring the least code change/complete query rewrite.
However, full credit is due to #slaakso's post here in this topic for analysing the problem. Note that he states that the behaviour has been fixed/altered in MySQL 8 such that this workaround is unnecessary, so it may only be necessary in MySQL 5.7 or earlier.

MySQL - Using the result of a Stored Procedure in an Inner Join

I have a MySQL Stored Procedure that returns multiple rows. Is there a way to use this result in an Inner Join with another table? I've tried:
SELECT ErrorMessage FROM ErrorMessage em
INNER JOIN User_Language(pCompanyID, pUserID) l ON em.Language=l.LanguageID
WHERE ErrorCode = pErrorCode
ORDER BY l.LanguageOrder LIMIT 1;
In this example, User_Language is the stored procedure that returns a list of languages in order of preference. The intent is to return an error message in the user's preferred language.
I found a viable solution. Within the User_Language stored proc, I create a Temp table (called UserLanguages) which I can then use in the inner join.
CALL User_Language(pCompanyID, pUserID);
SELECT ErrorMessage INTO vErrorMessage FROM ErrorMessage em
INNER JOIN UserLanguages l ON em.Language=l.LanguageID
WHERE ErrorCode = pErrorCode
ORDER BY l.LanguageOrder LIMIT 1;

Taking one column from MySQL joined tables

I have a query in MySQL and I am making a crystal report by using this.
Now inside the query i have a column called scan_mode and it is coming from gfi_transaction table. This scan_mode I am using in report to suppress some sections. But some times this value is coming null for some transaction ids.
So now I want to take this scan_mode as separate query so that it will work.
Can any one please help how I can modify the below query to take only scan_mode column.
SELECT
cc.cost_center_code AS cccde,
cc.name AS ccnme,gf.scan_mode,
cc.cost_center_id AS ccid,
site.name AS siteme,
crncy.currency_locale AS currency_locale,
cntry.language AS LANGUAGE,
cntry.country_name AS cntrynm,
crncy.decimal_digits AS rnd,
gf.transaction_no AS Serial_No,
brnd.name AS brand_name,
rsn.description AS reason,
gf.comment AS COMMENT,
ts.status_description AS STATUS,
DATE_FORMAT(gf.created_date,'%d/%m/%Y') AS created_date,
gf.created_by AS created_by,
IFNULL(gf.approval_no,'Not authorized') AS Trans_no,
gf.approved_date AS approval_dt,
gf.approved_by AS approved_by,gf.status AS status1,
IFNULL(loc.cost_center_code,cc.cost_center_code) AS cur_location,
gf.document_ref_no,gf.document_ref_type,
,DATE_FORMAT(document_ref_date1,'%d/%m/%Y')) AS invoice_no
FROM
gfi_transaction gf
INNER JOIN gfi_instruction gfn ON (gf.transaction_id=gfn.transaction_id)
INNER JOIN gfi_document_instruction doc ON (gf.ref_transaction_no = doc.document_instruction_id)
INNER JOIN reason rsn ON (gf.reason_id = rsn.reason_id)
INNER JOIN gfi_status ts ON (gf.status = ts.gfi_status_id)
INNER JOIN transaction_type tt ON (gf.transaction_type_id = tt.transaction_type_id)
INNER JOIN brand brnd ON(gf.brand_id=brnd.brand_id)
-- cc details
INNER JOIN cost_center cc ON (brnd.parent_brand = cc.brand_id OR gf.brand_id = cc.brand_id)
INNER JOIN site site ON(cc.site_id = site.site_id)
INNER JOIN country cntry ON (site.country_id = cntry.country_id)
INNER JOIN currency crncy ON (cntry.currency_id=crncy.currency_id)
LEFT OUTER JOIN alshaya_location_details loc ON
(gf.brand_id = loc.brand_id AND loc.cost_center_id = gf.cost_centre_id)
LEFT OUTER JOIN alshaya_location_details locto ON
(locto.cost_center_id = gf.from_cost_center_id)
WHERE
gf.transaction_id='{?TransID}'
AND rsn.transaction_type_id IN (10,11,14)
wow, that's a big query. I ran across a similar problem in a query i was building and found the if syntax to be a solution to my problem. This was also answered in this question: MYSQL SELECT WITHIN IF Statement
$psdb->query = "SELECT count, s.classid,
if (k.sic != k.siccode, k.siccode, s.siccode) as siccode,
if (k.sic != k.siccode, k.sicdesc, s.sicdesc) as sicdesc,
if (k.sic != k.siccode, k.sicslug, s.sicslug) as sicslug
FROM ...
It looks like scan_mode column comes from "gfi_transaction" table which seems to be primary table in your query. If you get null for this column then it means your table itself have NULL value for this column. Taking that separately in a query wont solve your problem. Try replacing null with a default value and handle it in code. You can add default value instead of NULL by using ifnull(scan_mode, 'default')

Set some values to NULL if.. other values are null

I have this medium-sized query and I am having some problems getting certain fields.
SELECT DISTINCT
enc.id, enc.cus_id, enc.createdon, enc.status,
enc.segment, enc.currentstep, enc.groupid, enc.fdprotocol,
enc_task.linkfile, cus.fname, cus.lname, login.first_name,
login.last_name, login.username, login.user_code, fp.protocol
FROM
mob_encounters_task enc_task, mob_encounters enc,
mob_customer cus, mob_login login, mob_protocol_type fp
WHERE
enc.id=enc_task.encounterid
AND
cus.id=enc_task.cus_id
AND
login.id=enc.createdby
GROUP BY enc.id
fp.protocol is a string, and on the table fp there are up to 5 or 6 "protocols".
what I wanted to do is if enc.fdprotocol is empty then fp.protocol should be empty, otherwise get the fp.protocol associated to the fp.id= enc.fdprotocol
Please let me know if this sounds confusing, I have been stuck on this for some time
I find that using the actual JOIN syntax makes queries much more readable and maintainable. In this case you need to use a LEFT JOIN and you have to change your syntax for that:
SELECT enc.id, enc.cus_id, enc.createdon, enc.status, enc.segment,
enc.currentstep, enc.groupid, enc.fdprotocol, enc_task.linkfile, cus.fname,
cus.lname, login.first_name, login.last_name, login.username, login.user_code,
fp.protocol
FROM mob_encounters_task enc_task
JOIN mob_encounters enc ON enc.id=enc_task.encounterid
JOIN mob_customer cus ON cus.id=enc_task.cus_id
JOIN mob_login login ON login.id=enc.createdby
LEFT JOIN mob_protocol_type fp ON fp.id = enc.fdprotocol
Also I don't believe you need DISTINCT

MySQL Stored Procedure Conditional Statement

Can anyone help me on how could I have an conditional statement on the MySQL stored procedure?
I have this as sample query,
If my stored procedure parameter isFirstTask = true then I will use LEFT JOIN else I will use INNER JOIN
SELECT *
FROM jobdetails jd
INNER JOIN taskslogs tl ON jd.jobID = tl.jobid;
The question is how could change the INNER JOIN into LEFT JOIN without repeating the whole query just to replace one word. Assuming that my query is bulk. Is it possible? Some idea please.
SELECT *
FROM jobdetails jd
LEFT JOIN taskslogs tl ON jd.jobID=tl.jobid
WHERE IF(isFirstTask = TRUE, TRUE, tl.jobid IS NOT NULL);
you can build the query as a string inside the stored procedure. While building the query, check the value of your parameter and decide which JOIN to use. Hope this helps.