Adding a new column for numbering in MySQL procedure - mysql

I want to make a stored procedure in MySQL that show a data of a table with a row number and not the id of the row. Like this:
And I tried this code:
CREATE DEFINER=`root`#`localhost` PROCEDURE `count_row`()
BEGIN
declare row_num INTEGER;
declare result INTEGER;
SELECT count(id) FROM engineers INTO row_num;
SET result=0;
WHILE(result<=row_num)
DO
SELECT result=result+1, name, date FROM engineers ;
END WHILE;
END
The result was creating undefined number of tabs in MySQL.
Then I tried this code:
CREATE DEFINER=`root`#`localhost` PROCEDURE `count_row`()
BEGIN
declare row_num INTEGER;
declare result INTEGER;
SELECT count(id) FROM engineers INTO row_num;
SET result=1;
WHILE(result<=row_num)
DO
SELECT result, name, date FROM engineers;
SET result = result +1;
END WHILE;
END
And I got 5 tabs (equal to total row number) like this:
second tab:
What I want is to show only one tab with one table as shown in the first picture of the question.

Try
SELECT #row_number:=#row_number+1 as RowNum, name as Engineer, date
FROM engineers, (SELECT #row_number:=0) as t
order by name;
or
SET #row_number:=0;
SELECT #row_number:=#row_number+1 as RowNum, name as Engineer, date
FROM engineers
order by name;

Related

Error Code: 1111. Invalid use of group function in stored procedure

Getting the error stated above.
I tried switching the wheres to havings in the while loop but have had no luck. I don't know if being in the while loop is breaking things or what?
The Goal is to bet a table with items that are available using data stored in the equip_ava temp table and subtracting based on if the item is out based the invoices.
BEGIN
DECLARE s int(5);
DECLARE m int(5);
DECLARE o date;
DECLARE i date;
DECLARE a int(3);
DECLARE e int(3);
DROP TABLE IF EXISTS equip_out;
CREATE TEMPORARY TABLE equip_out
SELECT line_items.invoice_number,date_out,date_due_in,equipment_qty,line_items.equipment_id FROM invoices join line_items
group by Sequence;
DROP TABLE IF EXISTS equip_ava;
CREATE TEMPORARY TABLE equip_ava
SELECT equipment_id,equipment_total FROM equipment
GROUP BY equipment_id;
SET #s = 1;
SET #m = MAX(line_items.Sequence);
WHILE #s <= #m DO
SELECT date_out INTO #o FROM invoices join line_items
WHERE Sequence=#s
GROUP BY Sequence;
SELECT date_due_in INTO #i FROM invoices join line_items
WHERE Sequence=#s
GROUP BY Sequence;
IF daily BETWEEN #o AND #i THEN
SELECT equipment_qty INTO #a FROM line_items
WHERE Sequence=#s
GROUP BY Sequence;
SELECT equipment_id INTO #e FROM line_items
WHERE Sequence=#s
GROUP BY Sequence;
UPDATE equip_ava
SET equipment_total=equipment_total-#a
WHERE equipment_id=#e;
END IF;
SET #s=#s+1;
END WHILE;
SELECT * FROM equip_ava;
END
You can't directly use an aggregation function in a stored procedure (or any SET statement for that matter); it needs to be part of a SELECT. You need to change that line to
SET #m = (SELECT MAX(Sequence) FROM line_items);
or
SELECT MAX(Sequence) INTO #m FROM line_items;
Note that you have declared a lot of variables but are not actually using them. #s is a global variable, s is the local variable in your procedure.
Found the issue need to change
SET #m = MAX(line_items.Sequence);
to
SELECT #m = MAX(line_items.Sequence) FROM line_items;

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.

Debugging stored procedure in SQL group function

[
For the cname='Liam', there are 3 card_id
i.e. 87260101, 87260153, 87501026
We want to find out how many extra card_id this person has, means we want output=2.
For the cname='Elizabeth', there are 2 card_id.
i.e.87501022, 87501000 and we want an output of 1.
For the others who only has one card_id which correspond to an empty loss_report_date,
count(loss_report_date)=0, the output=0
For the code below, we get the error code=1111, invalid use of group function, but the store procedure does not report any error when we run procedure alone.
The DBMS we are using is Innodb.
DELIMITER $$
CREATE PROCEDURE Gettransaction(IN cname varchar(50), OUT cnt int)
BEGIN
#select card_id if there is no old_id, if t1.card_id=t2.old_id then use card_id
DECLARE maxcardid char(8);
DECLARE cnt int default 1;
DECLARE nextMax int;
set maxcardid = (select max(card_id) from card
group by cname);
WHILE cname in (select cname from card where count(loss_report_date)>=1)
DO
set nextMax = (select max(card_id)
from card
where card_id < maxcardid);
set cnt = cnt + 1;
set maxcardid = nextMax;
END WHILE;
END$$
DELIMITER ;
call Gettransaction('Liam', #output);
select #output

MySQL stored procedure pass select as parameter

could you please give me an advice how to CALL prcd with SELECT results? Or advice me pls better solution.. I am open minded to all working solution
I have a procedure to control inserting data ...
CREATE PROCEDURE control_insert (
)
And I need to pass data from SELECT results to procedure ...
SELECT t.c1, t.c2
FROM table t1
LEFT JOIN other_table t2
ON t1.id = t2.id
WHERE 1=1
The point is, I need to get some data via SELECT (around 6 tables joined to the base table) and I need to do control for each row before insert.. each row should meet some conditions .. if it doesn't meet them, it should just skip it and process next one ...
The procedure should look like:
CREATE PROCEDURE control_insert (
IN v_c1 INT,
IN v_c2 INT
)
BEGIN
IF v_c1 > 1 THEN
INSERT INTO controlled_table (id, type) VALUES (v_c1, v_c2);
ELSE
/* do nothing */
END IF;
END;
CALL control_insert ( SELECT .... );
Could you help me with that? Is there any possibility to do this via MySQL? I can write a PERL skript, but I want to avoid this type of solution ... I just one to do it only in MySQL way
Thank you
EDIT1: I need to check if ID of the SELECT result and LABEL is already in this table for specific date ... this code above is only an example to demonstrate the situation
SOLUTION
I've found the solution ... so for the other visitors:
calling procedure:
CALL controlInsert();
procedure body:
CREATE PROCEDURE controlInsert()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE v_id INT;
DECLARE v_id_dupl INT;
DECLARE v_label INT;
DECLARE v_date DATE;
DECLARE v_type VARCHAR(100);
DECLARE v_category VARCHAR(255);
DECLARE v_user VARCHAR(255);
DECLARE v_country VARCHAR(255);
DECLARE c1 CURSOR FOR SELECT id, label, date, type, category, user, country FROM t1 LEFT JOIN ... /* whole select with 6 joins ended by ; */
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
## open cursor
OPEN c1;
## loop through the cursor
read_loop: LOOP
## fetch cursor into variables
FETCH c1 INTO v_id , v_label, v_date, v_type, v_category, v_user, v_country;
## check if there is any record
IF done THEN
LEAVE read_loop;
END IF;
## get count of existing records
SELECT count(*) INTO v_id_dupl
FROM
WHERE 1=1
AND id = v_id
AND label= v_label
AND date = v_date;
## if v_id_dupl = 0 => no rows found (ok to load)
IF (v_id_dupl = 0) THEN
INSERT INTO target_table (id, label, date, type, category, user, country)
VALUES (v_id , v_label, v_date, v_type, v_category, v_user, v_country);
END IF;
END LOOP;
CLOSE c1;
END
If that is all your stored procedure is doing, then you don't actually need it. You can do the whole thing in a single statement:
INSERT INTO controlled_table (id, type)
SELECT t.c1, t.c2
FROM table t1
LEFT JOIN other_table t2 ON t1.id = t2.id
WHERE something = somethingElse
AND t.c1 > 1
Essentially, I've just combined your original query with the INSERT statement in your procedure.
If your procedure is more complex and needs to do multiple operations on each row, then you should look into using a cursor.

Arithmetic operation in MySQL through Procedure

I have a table t1 which have a column Marks in this column values are 10, 20, 30, 40.
Now I want to use a procedure to get this result:
Marks Total_Marks
10 10
20 30
30 60
40 100
DELIMITER //
CREATE PROCEDURE Total_Marks ( In Num Int(4) )
Begin
Declare Mark Int(4);
Declare Add_M Int(4);
DECLARE NO_MORE_ROWS BOOLEAN;
DECLARE DataCursor CURSOR FOR SELECT Marks
FROM t1 where marks = Num;
DECLARE DataCursor1 CURSOR FOR SELECT Sum(Marks) FROM t1;
OPEN DataCursor;
FETCH DataCursor INTO Mark;
CLOSE DataCursor;
OPEN DataCursor1;
READ_LOOP1: LOOP
FETCH DataCursor1 INTO Add_M;
IF NO_MORE_ROWS THEN
LEAVE READ_LOOP1;
END IF;
BEGIN
SET Add_M = SUM(Mark);
END;
END LOOP READ_LOOP1;
CLOSE dataCursor1;
SET NO_MORE_ROWS = FALSE;
end //
DELIMITER ;
You don't need a procedure at all, let alone cursors. But what you need, is a column that defines the order of the rows as mentioned in the comments.
create table foo (id int auto_increment primary key, bar int);
insert into foo(bar) values (10), (20), (30), (40);
In this example I introduced the column id for that matter. Or you can of course just order by your marks or whatever suits your needs.
select
bar
, #total := #total + bar as my_total
from
foo
, (select #total := 0) var_init
order by id
see it working live in this sqlfiddle
As explanation, with this cross joined query
, (select #total := 0) var_init
we initialize our variable holding the running total #total. It's the same as writing
set #total = 0;
select
bar
, #total := #total + bar as my_total
from
foo
order by id;
The rest is self explaining I guess.
You can read more about these type of variables here.
UPDATE (for completeness):
Here are two other possibilities how to solve it without variables. Although I like variables usually better, cause in this one
select
t1.bar
, sum(t2.bar)
from
foo t1
inner join foo t2 on t1.id >= t2.id
group by t1.id;
you end up with a potentially huge temporary table, since you join every row to all previous rows and then calculate the sum.
And in this solution
select
bar
, (select sum(bar) from foo sf where sf.id <= foo.id) as my_total
from
foo;
you have a dependent subquery executed for each row. This is even worse than the previous solution.
I posted those just for completeness and if you really can't use variables (because of creating a view for example).