SELECT INTO with LIMIT - mysql

If I have a table defined like so: mytable(model char(4), price int); and I'm trying to find the value closest to price given a base price, how would I go about it?
What I have is this:
delimiter //
CREATE FUNCTION findClosestPrice (value int) returns int
BEGIN
DECLARE closestPrice int default -1;
DECLARE row int default 0;
DECLARE rows int default 0;
DECLARE currPrice int default -1;
select count(*) from mytable into rows;
SET row=0;
WHILE row < rows DO
select price from mytable limit row, 1 into currPrice; <----- this gives an error
SET row = row + 1;
END WHILE;
END//
Why is it that I can't have that line?
How do I go about selecting the price from a current row in a loop and saving it into a variable that I've declared? I thought this would work but it does not.

You can find the closest price to a base price with a single query:
SELECT model,price,(ABS(price-basePrice)) AS price_diff FROM mytable ORDER BY price_diff LIMIT 1

into must follow the column names before from.
Change:
select price from mytable limit row, 1 into currPrice;
To:
select price into currPrice from mytable limit row, 1;
Refer to:
SELECT Syntax
... the INTO clause can appear either as shown in
the syntax description or immediately following the select_expr list
...

Related

SQL Cursor to determine median value

I am trying to write a Stored Procedure to retrieve the median salary from a table and am having trouble figuring out how to retrieve the data from the cursor.
Currently my code is:
DELIMITER //
CREATE PROCEDURE MedianSalary(OUT median INT)
BEGIN
DECLARE counter int(5) DEFAULT 0;
DECLARE set_size int(5) DEFAULT (SELECT count(*) from employee);
DECLARE median_index int(5) DEFAULT (SELECT floor(count/2));
DECLARE all_salaries CURSOR
FOR SELECT salary from employee,
OPEN all_salaries;
WHILE #counter != #median_index
BEGIN
SET #counter = #counter + 1,
FETCH NEXT from all_salaries,
END;
FETCH all_salaries INTO median;
CLOSE all_salaries;
END //
DELIMITER ;
I can't seem to find any documentation similar to what I am trying to achieve, any help would be greatly appreciated.
I don't have an answer to your stored procedure problem, but note that we can actually find the median from a table in MySQL fairly easily using session variables to simulate the row number:
SET #row_number = 0;
SET #row_count = (SELECT COUNT(*) FROM yourtable);
SELECT AVG(salary) AS median
FROM
(
SELECT (#row_number:=#row_number + 1) AS rn, salary
FROM yourTable
ORDER BY salary
) t
WHERE
(#row_count % 2 = 0 AND rn IN (#row_count / 2, (#row_count / 2) + 1) OR
#row_count % 2 <> 0 AND rn = #row_count / 2);
Demo
Note the ugliness in the WHERE clause has to do with the edge case of your table having an even number of records. In this case, there technically is not a single median record, so instead I report the mean of the two records which sit about the median on either side.

SQL: GROUP BY Clause for Comma Separated Values

Can anyone help me how to check duplicate values from multiple comma separated value. I have a customer table and in that one can insert multiple comma separated contact number and I want to check duplicate values from last five digits.For reference check screenshot attached and the required output is
contact_no. count
97359506775 -- 2
390558073039-- 1
904462511251-- 1
I would advise you to redesign your database schema, if possible. Your current database violates First Normal Form since your attribute values are not indivisible.
Create a table where id together with a single phone number constitutes a key, this constraint enforces that no duplicates occur.
I don't remember much but I will try to put the idea (it's something which I had used a long time ago):
Create a table value function which will take the id and phone number as input and then generate a table with id and phone numbers and return it.
Use this function in query passing id and phone number. The query is such that for each id you get as many rows as the phone numbers. CROSS APPLY/OUTER APPLY needs to be used.
Then you can check for the duplicates.
The function would be something like this:
CREATE FUNCTION udf_PhoneNumbers
(
#Id INT
,#Phone VARCHAR(300)
) RETURNS #PhonesTable TABLE(Id INT, Phone VARCHAR(50))
BEGIN
DECLARE #CommaIndex INT
DECLARE #CurrentPosition INT
DECLARE #StringLength INT
DECLARE #PhoneNumber VARCHAR(50)
SELECT #StringLength = LEN(#Phone)
SELECT #CommaIndex = -1
SELECT #CurrentPosition = 1
--index is 1 based
WHILE #CommaIndex < #StringLength AND #CommaIndex <> 0
BEGIN
SELECT #CommaIndex = CHARINDEX(',', #Phone, #CurrentPosition)
IF #CommaIndex <> 0
SELECT #PhoneNumber = SUBSTRING(#Phone, #CurrentPosition, #CommaIndex - #CurrentPosition)
ELSE
SELECT #PhoneNumber = SUBSTRING(#Phone, #CurrentPosition, #StringLength - #CurrentPosition + 1)
SELECT #CurrentPosition = #CommaIndex + 1
INSERT INTO #UsersTable VALUES(#Id, #PhoneNumber)
END
RETURN
END
Then run CROSS APPLY query:
SELECT
U.*
,UD.*
FROM yourtable U CROSS APPLY udf_PhoneNumbers(Userid, Phone) UD
This will give you the table on which you can run query to find duplicate.

WHILE LOOP not working as intended in mysql PROCEDURE

I have 100 rows in table tbl_master_sales and an empty table tbl_customer_sales.When I use WHILE loop to insert data from tbl_master_salesto tbl_customer_sales,it only inserts 50 rows.However,it should have insert 100 rows taking two iteration of while loop.What may be my mistake in following PROCEDURE:
CREATE PROCEDURE ROWPERROW()
BEGIN
DECLARE n INT DEFAULT 0;
DECLARE i INT DEFAULT 0;
SELECT COUNT(*) FROM tbl_master_sales INTO n;
SET i=0;
WHILE i<n DO
INSERT INTO tbl_customer_sales (id,card_number,customer_name,customer_phone,bill_no,item_code,division,section,department,item_name,store,promo_name,billdiscount_name,billqty,promo_amount,bill_discount_amount,loyaltyamount,net_amount)
SELECT id, card_number, customer_name, customer_mobile, billno, itemcode, division, section, department, itemname, store, promoname, billdiscountname, billqty, promoamount, billdiscountamount, loyaltyamount, netamount
FROM tbl_master_sales
WHERE NOT EXISTS(SELECT 1
FROM tbl_customer_sales
WHERE id=tbl_master_sales.id)
LIMIT i,50;
SET i = i + 50;
END WHILE;
End;;
I don't see anything wrong with your procedure code logic but the reason for inserting only 50 rows could be the NOT EXISTS part shown below, which is restricting from inserting duplicate rows (or) filtering out the rest records.
WHERE NOT EXISTS(
SELECT 1
FROM tbl_customer_sales
WHERE id=tbl_master_sales.id)

MySql Stored Proc - Select query returns null if using where clause not included in select list?

I have a proc that simply contains the following:
SELECT Col1, Col2 FROM table WHERE Id = 1;
This always returns null values. If I change the statement to:
SELECT Id, Col1, Cold2 FROM table WHERE Id = 1;
The row gets returned as expected. Does mySql procs demand that the where clause columns appear in the select list? It doesn't have this behaviour when running the SQL directly in phpMyAdmin.
Here's the code in question:
CREATE PROCEDURE sp_player_login
(
IN
userGraphId INT(11),
authToken TEXT
)
BEGIN
DECLARE playerId INT;
DECLARE newPlayer BOOLEAN DEFAULT FALSE;
SELECT Id INTO playerId FROM player WHERE FacebookGraphId = userGraphId;
If playerId IS NULL THEN
-- Create the player.
SET newPlayer = TRUE;
-- Get new player defaults.
SELECT NewPlayerTurns, NewPlayerMoney, NewPlayerMorale, NewPlayerMissilePower FROM defaults WHERE Id = 1;
END IF;
END #
The second SELECT NewPlayerTurns... is the one that returns null values if I don't specify the Id column in the select clause. If I remove the first SELECT Id INTO ...the second query works without the Id in the select clause...? confused

referring to the already fetched results in a query within the same query

So suppose I have this table with 4 columns:
id content parent timestamp
whereby the parent column refers to an id of another entry in the table
I want to accomplish the following:
Select the first 50 rows from the table Ordered by the following:
for each row,
if(parent = 0){
add row to resultset, ordered by timestamp
}
else if (parent != 0){
if parent is in the list of rows already fetched so far by the query,
add row to resultset, ordered by the timestamp
otherwise, wait until the parent gets fetched by the query
(assuming it gets fetched at all since there we're only getting the first 50 rows)
}
this ordering logic is somewhat complicated, and I'm wondering if it's even possible to accomplish this using MYSQL ORDER BY statement in a single query WITHOUT having to resort to subqueries? Perhaps we could set and use variables? But how would the ORDER BY statement will be implemented?
Here's a solution using variables which I incorporated into a procedure with a loop using an outer join and a temporary table to hold the unused inputdata but no subqueries.
Delete any old version of the procedure and set the delimiter
DROP PROCEDURE IF EXISTS order_proc;
DELIMITER ;;
Start writing the procedure
CREATE PROCEDURE order_proc()
BEGIN
DECLARE n INT DEFAULT 50;
DECLARE m INT DEFAULT 0;
DECLARE i INT DEFAULT 0;
DECLARE custom_limit INT DEFAULT 0;
DROP TABLE IF EXISTS pre_resultset;
CREATE TABLE pre_resultset LIKE input_data;
ALTER TABLE pre_resultset MODIFY id INT;
ALTER TABLE pre_resultset DROP PRIMARY KEY;
ALTER TABLE pre_resultset ADD COLUMN iid INT PRIMARY KEY NOT NULL AUTO_INCREMENT FIRST;
SELECT n INTO custom_limit;
Set SQL_SELECT_LIMIT = custom_limit;
INSERT INTO pre_resultset SELECT NULL,id, content, parent, timestamp FROM input_data WHERE parent = 0 ORDER BY timestamp;
Set SQL_SELECT_LIMIT = default;
DROP TABLE IF EXISTS unused_input_data;
CREATE TABLE unused_input_data LIKE input_data;
ALTER TABLE unused_input_data ADD null_col VARCHAR(1);
SELECT COUNT(*) FROM pre_resultset INTO m;
WHILE m<n DO
SELECT COUNT(*) FROM pre_resultset INTO m;
TRUNCATE unused_input_data;
INSERT INTO unused_input_data SELECT input_data.id, input_data.content, input_data.parent, input_data.timestamp, pre_resultset.id AS null_col FROM input_data LEFT OUTER JOIN pre_resultset on input_data.id = pre_resultset.id WHERE pre_resultset.id IS NULL ;
SELECT n-m INTO custom_limit;
Set SQL_SELECT_LIMIT = custom_limit;
INSERT INTO pre_resultset SELECT NULL, unused_input_data.id, unused_input_data.content, unused_input_data.parent, unused_input_data.timestamp FROM unused_input_data JOIN pre_resultset WHERE unused_input_data.parent = pre_resultset.id ORDER BY unused_input_data.timestamp;
Set SQL_SELECT_LIMIT = default;
SELECT COUNT(*) FROM pre_resultset INTO i;
SELECT (IF( i = m, n, i)) INTO m;
END WHILE;
SELECT id, content, parent, timestamp FROM pre_resultset AS resultset;
DROP TABLE IF EXISTS pre_resultset;
DROP TABLE IF EXISTS unused_input_data;
End;
;;
Change the delimeter back
DELIMITER ;
Run the procedure
CALL order_proc();