Creating Stored Procedures and joining tables - mysql

I am using a database called salesshort used mostly for learning MySQL. I am trying to create a stored procedure but for some odd reason when I run my code is not even creating the store procedure I think my syntax is wrong.
here are the instructions and my code.
Create a stored procedure ‘gbSaleP’ that returns the following ‘order types’:
"we are loosing money" if the actual profit for each order (i.e., OrderNumber)- is lower than or equal to zero; "good sale" if the difference between potential profit and actual profit is $2500 or lower; and
"bad sale" if the difference between potential profit and actual profit is greater than $2500.
delimiter //
create procedure gbSaleP ( in orderNumber int(10), out SaleStatus varchar (40))
begin
set gbsaleP = (select sum(o.quantityOrdered*p.MSRP - o.quantityOrdered*p.buyPrice) - abs(sum(o.quantityOrdered*o.priceEach - o.quantityOrdered*p.buyPrice))
from orderdetails as o
join products as p
using(productCode)
group by orderNumber
having porderNumber = orderName);
if gbSaleP <= 2500 then set SalesStatus = "good sale"
elseif gbSaleP => 2500 then set SaleStatus = "bad sale"
else set gbSaleP = "unknown"
end if;
end;
delimiter //

If your amount calculation script is right, you may use below.
DELIMITER //
CREATE procedure gbSaleP (IN orderNumber int(10), OUT SaleStatus varchar (40))
BEGIN
DECLARE amount DECIMAL(20,2);
set amount = (/*.......Correct SQL script*/);
if amount <= 2500 then
set SaleStatus = 'good sale';
elseif amount > 2500 then
set SaleStatus = 'bad sale';
else
set SaleStatus = 'unknown';
end if;
END; //
DELIMITER ;
You can test it with:
CALL `gbSaleP` (7, #`SaleStatus`);
SELECT #SaleStatus;

Related

MySQL Stored Procedure Case Statement Syntax Error

I'm trying to create a stored procedure that calculates the amount by multiplying the ordered quantity & the rate of the product from different tables called Rate and getting Quantity from the Bookings Table.After calculating I want it to be inserted into another Table 'Amount'. I am having totally 5 products. If for example product = 'abc', then put amount for 'abc' and insert zero for the rest of the products' amount. Herein, I am using a Case Statement which throws syntax error. Kindly please offer me your help.
DELIMITER $$
CREATE PROCEDURE `calculate_amount` (IN IN_book_id INT,IN IN_qty INT )
-- begin
BEGIN
-- declare
DECLARE prdct VARCHAR(10);
DECLARE cust_id INT(5);
-- select into
SELECT Product FROM Bookings WHERE Book_id=IN_book_id INTO prdct;
SELECT Cust_id FROM Bookings WHERE Book_id=IN_book_id INTO cust_id;
-- conditionals & action
CASE prdct
WHEN "20ltr"
THEN INSERT INTO Amount(Cust_id,Book_id,Can,300ml,500ml,1lit,2lit) VALUES(cust_id,IN_book_id,(SELECT Rate.Can*Bookings.Qty FROM Rate,Bookings WHERE Bookings.Book_id=IN_book_id),0,0,0,0);
WHEN "300ml"
THEN INSERT INTO Amount(Cust_id,Book_id,Can,300ml,500ml,1lit,2lit) VALUES(cust_id,IN_book_id,0,(SELECT Rate.300ml*Bookings.Qty FROM Rate,Bookings WHERE Bookings.Book_id=IN_book_id),0,0,0);
WHEN "500ml"
THEN
INSERT INTO Amount(Cust_id,Book_id,Can,300ml,500ml,1lit,2lit) VALUES(cust_id,IN_book_id,0,0,(SELECT Rate.500ml*Bookings.Qty FROM Rate,Bookings WHERE Bookings.Book_id=IN_book_id),0,0);
WHEN "1ltr"
THEN
INSERT INTO Amount(Cust_id,Book_id,Can,300ml,500ml,1lit,2lit) VALUES(cust_id,IN_book_id,0,0,0,(SELECT Rate.1lit*Bookings.Qty FROM Rate,Bookings WHERE Bookings.Book_id=IN_book_id),0);
WHEN "2ltr"
THEN
INSERT INTO Amount(Cust_id,Book_id,Can,300ml,500ml,1lit,2lit) VALUES(cust_id,IN_book_id,0,0,0,0,(SELECT Rate.2lit*Bookings.Qty FROM Rate,Bookings WHERE Bookings.Book_id=IN_book_id));
ELSE
BEGIN
END;
END CASE;
-- end
END;$$
DELIMITER ;
In SQL (in general), CASE is not used for program conditional flow, it is used to return singular values in variable assignment. MySQL does allow it but I recommend to avoid using it as other database systems don't support it
Typical scenarios:
--doing varied logic:
name = CASE
WHEN num = 1 THEN 'one'
WHEN othernum = 2 THEN 'two'
END
--testing a single variable for being equal to values:
name = CASE num
WHEN 1 THEN 'one'
WHEN 2 THEN 'two'
END
Avoid this:
CASE
WHEN num = 1 THEN name = 'one'
WHEN num = 2 THEN name = 'two'
END
If you're after keeping your database skills cross portable, essentially the only thing you should put inside the "result" of a case is a value, not a statement
IF ELSEIF ELSE is used for conditional program flow in major databases, the MySQL docs for it are here:
https://dev.mysql.com/doc/refman/8.0/en/if.html
As another aside, prefer use of ' for strings, not " - double quotes are another MySQL deviation from standard

Use MySQL query result in a loop

I just started working with SQL and have been working with MySQL.I am trying to write a stored procedure that will take each value from my buyPrice column in my products table, and store each value into a variable. I then want it to multiply this variable by the sales tax and then take each result and place it into my empty sales_tax column. I would like to populate the whole column with the sales tax for each item. When I execute this method I get some error saying the productCode doesn't have a default value. What is the proper way to write this? I know this isn't the most efficient way of doing this task, just trying to practice.
DELIMITER //
CREATE PROCEDURE nFirstProcedure()
BEGIN
DECLARE IdValue, counter, holdValue, result INT DEFAULT 0;
DECLARE holdName VARCHAR(30);
SET counter = 1;
WHILE counter < ( SELECT COUNT(*) FROM products)
DO
SET holdValue = (SELECT buyPrice FROM products WHERE sales_tax = null);
SET result = (holdValue * 0.08);
INSERT INTO products (sales_tax) VALUES (result);
END WHILE;
END//
DELIMITER ;
You should not do this with a loop. Just use update:
update products
set sales_tax = buyPrice * 0.08
where sales_tax is null;

Select in MySQL stored procedure not returning values

I have a stored procedure, shown below, which I created to add dollar sales to a table (WeeklySales) which currently stores only unit sales. The cursor operates on on the WeeklySales table. The pricing data is stored in the Pricing table. The Pricing table actually contains changes in prices. The effective date for a price change is stored in Pricing.effectiveDate, so I have to find the pricing which was effective for the week in which the unit was sold (which is stored in WeeklySales.weekStart).
The problem I'm having is that the first select after the IF doesn't return anything. I've confirmed that this select does return a value when I run it outside of the procedure using the values which it would be called with inside the procedure. I'm not sure what's wrong here, but I'm guessing maybe this has to do with the fact that the this select is operating on a table which is different from the cursor? Anyone know? Is there a better way to do this?
DELIMITER //
CREATE PROCEDURE `createWeeklyPricing` (IN startDate DATE, IN endDate DATE)
BEGIN
--
-- Populate the proceeds column using the Pricing table
DECLARE product VARCHAR(255);
DECLARE weekStart DATE;
DECLARE units, done INT;
DECLARE proceeds DECIMAL(6,2);
DECLARE effectiveDate DATE;
DECLARE currentRow CURSOR FOR SELECT `weekStart`, `product`, `units` FROM `WeeklySales` WHERE `weekStart` >= startDate AND `weekStart` <= endDate;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN currentRow;
SET done = 0;
WHILE done = 0 DO
FETCH currentRow INTO weekStart, product, units;
IF done = 0 THEN
SELECT MAX(`effectiveDate`) FROM `Pricing` WHERE `effectiveDate` <= weekStart AND `product` = product INTO effectiveDate;
SELECT `proceeds` FROM `Pricing` WHERE `effectiveDate` = effectiveDate AND `product` = product INTO proceeds;
UPDATE `WeeklySales` SET `proceeds` = units * proceeds WHERE `weekStart` = weekStart AND `product` = product;
END IF;
END WHILE;
CLOSE currentRow;
END//
echo (select) weekstart before the if statement...
If it returns null change the select FROM WeeklySales WHERE weekStart between startDate AND endDate
you need to use the INTO before FROM and variable needs '#' sign
change it to
SELECT MAX(`effectiveDate`) INTO #effectiveDate FROM `Pricing` WHERE `effectiveDate` <= weekStart AND `product` = product ;
hope this helps
This is because your variable name is overwriting your column name:
You have a variable named 'effectiveDate'
You have a column named 'effectiveDate'
SELECT MAX(`effectiveDate`) ...
Is MAX-ing the variable effectiveDate, not the column
Try naming the variable maxEffectiveDate
Beware that variables are case insensitive. This happened to me when i tried to select column IsBackUp into variable isBackUp (notice the i).

MySQL - creating a user-defined function for a custom sort

I'm working with a large set of legacy data (converted from a flat-file db), where a field is formatted as the last 2 digits of the year the record was entered, followed by a 4 digit increment...
e.g., the third record created in 1998 would be "980003", and the eleventh record created in 2004 would be "040011".
i can not change these values - they exist through their company, are registered with the state, clients, etc. I know it'd be great to separate out the year and the rest of it into separate columns, but that's not possible. i can't even really do it "internally" since each row has about 300 fields that are all sortable, and they're very used to working with this field as a record identifier.
so i'm trying to implement a MySQL UDF (for the first time) to sort. The query executes successfully, and it allows me to "select whatever from table order by custom_sort(whatever)", but the order is not what i'd expect.
Here's what I'm using:
DELIMITER //
CREATE FUNCTION custom_sort(id VARCHAR(8))
RETURNS INT
READS SQL DATA
DETERMINISTIC
BEGIN
DECLARE year VARCHAR(2);
DECLARE balance VARCHAR(6);
DECLARE stringValue VARCHAR(8);
SET year = SUBSTRING(0, 2, id);
SET balance = SUBSTRING(2, 6, id);
IF(year <= 96) THEN
SET stringValue = CONCAT('20', year, balance);
ELSE
SET stringValue = CONCAT('19', year, balance);
END IF;
RETURN CAST(stringValue as UNSIGNED);
END//
The records only go back to 96 (thus the arbitrary "if first 2 characters are less than 96, prepend '20' otherwise prepend '19'). I'm not thrilled with this bit, but don't believe that's where the core problem is.
To throw another wrench in the works, it turns out that 1996 and 1997 are both 5 digits, following the same pattern described above but instead of a 4 digit increment, it's a 3 digit increment. Again, I suspect this will be a problem, but is not the core problem.
An example of the returns I'm getting with this custom_sort:
001471
051047
080628
040285
110877
020867
090744
001537
051111
080692
040349
110941
020931
090808
001603
051175
I really have no idea what I'm doing here and have never used MySQL for a UDF like this - any help would be appreciated.
TYIA
/EDIT typo
/EDIT 2 concat needed "year" value added - still getting same results
You have some problems with your substrings, and the cast to int at the end makes it sort values with more digits at the end, not by year. This should work better;
DELIMITER //
CREATE FUNCTION custom_sort(id VARCHAR(8))
RETURNS VARCHAR(10)
READS SQL DATA
DETERMINISTIC
BEGIN
DECLARE year VARCHAR(2);
DECLARE balance VARCHAR(6);
DECLARE stringValue VARCHAR(10);
SET year = SUBSTRING(id, 1, 2);
SET balance = SUBSTRING(id, 3, 6);
IF(year <= 96) THEN
SET stringValue = CONCAT('20', year, balance);
ELSE
SET stringValue = CONCAT('19', year, balance);
END IF;
RETURN stringValue;
END//
DELIMITER ;
This can be simplified a bit to;
DELIMITER //
CREATE FUNCTION custom_sort(id VARCHAR(8))
RETURNS varchar(10)
DETERMINISTIC
BEGIN
IF(SUBSTRING(id, 1, 2) <= '96') THEN
RETURN CONCAT('20', id);
ELSE
RETURN CONCAT('19', id);
END IF;
END//
DELIMITER ;

Stored Procedure (mysql) fails with "can't return a result set in the given context"

I'm trying to get this SP to return (leave) if some conditions fails and so forth.
This code validates and it saves the procedure, but when I call the procedure with:
CALL ACH_Deposit(30027616,3300012003,200.00,"USD", "127.0.0.1")
It fails with error: "Procedure can't return a result set in the given context"
Does anyone have any idea on what the error is?
Procedure code:
CREATE DEFINER=`redpass_web_urs`#`%` PROCEDURE `ACH_Deposit`(
IN __Account_ID BIGINT,
IN __To_Bank_Account BIGINT,
IN __Amount DECIMAL(10,2),
IN __Currency CHAR(3),
IN __IP_Address VARCHAR(50)
)
COMMENT 'Makes a ACH deposit'
BEGIN
-- Declare Account Parameters
DECLARE _Account_Status INT;
DECLARE __Transaction_ID INT;
DECLARE _Account_Type INT DEFAULT 0;
DECLARE _Fee INT;
SELECT
Account_Status AS _Account_Status,
Account_Type AS _Account_Type
FROM Account_Users
WHERE Account_ID = __Account_ID;
main: BEGIN
-- Step 1, is account active ?
IF _Account_Status <> 1 THEN
-- Account must be active (not restricted, closed etc)
SELECT Response_Code, Description FROM ResponseCodes WHERE Response_Code = 106;
LEAVE main; -- Here we die..
END IF;
-- Step 2, Calculate the FEE (Loading Funds with ACH)
IF _Account_Type = 1 THEN
-- Personal Account
SET _Fee = (SELECT Fee_Template_Personal_1 FROM Fees WHERE Fee_Type_ID = __Fee_Type_ID);
ELSE
-- Business Account
SET _Fee = (SELECT Fee_Template_Business_1 FROM Fees WHERE Fee_Type_ID = __Fee_Type_ID);
END IF;
-- Step 3, Check that Fee is not bigger then the actual amount
IF _Fee > __Amount THEN
SELECT Response_Code, Description FROM ResponseCodes WHERE Response_Code = 108;
LEAVE main; -- Here we die..
END IF;
-- If we come here, we can make the transactions
INSERT INTO Bank_Transaction
(Bank_Account_ID
,Transaction_Type
,Amount
,IP_Address
,Pending)
VALUES
(__To_Bank_Account
,11
,__Amount
,__IP_Address
,1); -- Reserverade pengar
-- Transaction ID
SET __Transaction_ID = (SELECT LAST_INSERT_ID());
-- Deduct the Fee
INSERT INTO Bank_Transaction
(Bank_Account_ID
,Transaction_Type
,Amount
,Fee_Type_ID
,Fee_Transaction_ID)
VALUES
(__To_Bank_Account
,4
,-__Fee
,21
,__Transaction_ID);
END main;
SELECT Response_Code, Description, __Transaction_ID AS Transaction_ID
FROM ResponseCodes
WHERE Response_Code = 1;
END
To retrieve multiple resultsets from the stored procs, you should use a client which supports multiple queries.
If you use PHP, use MySQLi extension and call the procedure using mysqli_multi_query.
MySQL extension is only able to retrieve the first recordset returned by the proc. To be able to use ti, you should set CLIENT_MULTI_RESULTS (decimal 131072) in the parameter $client_flags to mysql_connect