MySQL stored procedure - Problem outputting values - sql

DROP PROCEDURE `uuu`//
CREATE DEFINER=`auth_tracker`#`%` PROCEDURE `uuu`()
BEGIN
DECLARE a,b CHAR(50);
DECLARE _output TEXT DEFAULT '';
DECLARE cur1 CURSOR FOR SELECT attribute_name, value
FROM user_product_attribute upa, product_attribute pa
WHERE upa.user_product_id IN
( SELECT upa.user_product_id
FROM user_product_attribute upa, user_product up, product_attribute pa, product p
WHERE pa.attribute_name = 'username'
AND pa.product_attribute_id = upa.product_attribute_id
AND pa.product_id = p.product_id
AND up.status = 'active'
AND p.product_name = 'broadband'
AND upa.value = 'lsolway-dsl' )
AND upa.product_attribute_id = pa.product_attribute_id;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO a, b;
SET _output = CONCAT(_output,a,b);
END LOOP;
SELECT _output;
END//
Hi guys, I am trying to get the SP to set the following output.. I cant see where i am going wrong.. Nothing is being returned..
The Query itself works fine standalone..

You're not defining any output parameters as far as I can tell. That would make it very difficult to get data back from a SQL stored procedure.

DECLARE an OUT param and stuff the value into that for output.
Also one suggestion, its always good to use # for your local variables in stored procedures. such as #_output, #a and #b.

The subquery is the reason.. I am only using one cursor for a query that would need two.. Im not even sure subqueries are possible in an SP..

Related

how to incorporate dynamic column name in sql query

I have a table, which has columns, say
Week1,Week2, Week3 and so on.
I have a stored procedure, and based on the number input, i want to select that column.
Example, if input is 4 then I want to make the query,
select *
from table_name
where Week4=<something>
Is there any way to do this other than using dynamic query? Because this dynamic thing will be just a small part of a huge query.
The comments about normalization are right, but if you have no choice, you can use "or" clauses:
declare #inputvalue int;
set #int = 1;
select *
from <table>
where (week1 = <something> and #inputvalue = 1)
or (week2 = <something> and #inputvalue = 2)
or (week3 = <something> and #inputvalue = 3)
or (week4 = <something> and #inputvalue = 4)
This will be very slow if the tables are of any size, as you won't be using any indexes. I wouldn't suggest doing this unless you're absolutely unable to change the table structure.
I realize this isn't what you asked for, but I figured I'd point out to some people who find this what you mean by doing this as a dynamic query.
You'd just write a procedure and hold the field name in there. Assuming that the naming standard is the same, so the input value would be the week# (1,2,7,27, 123, etc.) and the field name would directly correspond (Week1, Week2, Week7, Week27, Week123, etc.)
create or replace procedure myweek(week_in varchar2)
is
dyn_sql varchar2(1000);
begin
dyn_sql := 'select * from table_name where week'||week_in||' = ''something;'' '
execute immediate dyn_sql;
end;
/
Then to call it you'd just do something like :
exec myweek(27); and it would generate the sql:
select * from table_name where week27 = 'something';

SQL Server 2000: weird error: cannot convert VARCHAR to INT

I'm having a weird error that happens under weird circumstances.
The list of skill names I receive in the cursor curLongSkills is to be inserted into the table tbl_new_skill_overview if and only if they don't already exist. So I loop through the cursor as usual, and check whether it already exists before inserting.
The weird thing is that I receive the error Syntax error converting the varchar value 'Some Random Skill' to a column of data type int. on the line SELECT #iCount = COUNT(ID).
However, this does not happen if I remove the WHERE clause in that statement. So if I comment or remove WHERE Name = #sSkillName, it won't give the error. It's as if it thinks that I'm assigning #sSkillName to #iCount just because I'm using #sSkillName in the WHERE clause of the same query.
Other ways of doing this will suffice provided that I can tell whether or not the skill has already been inserted into tbl_new_skill_overview. I don't necessarily have to do it this way.
I've also tried the following, which gives the same error:
SET #iCount = (
SELECT COUNT(ID) AS Line_Count
FROM tbl_new_skill_overview
WHERE Name = #sSkillName
);
The server is running Microsoft SQL Server 2000 (I know, I know...).
Following is the entire SQL script.
DECLARE #sSkillName VARCHAR(200);
DECLARE #iCount INT;
DECLARE curLongSkills CURSOR FOR (
SELECT DISTINCT Name
FROM tbl_new_skill
WHERE Profile = 'long'
AND Parent_ID IS NULL
)
OPEN curLongSkills;
FETCH curLongSkills INTO #sSkillName;
WHILE ##FETCH_STATUS = 0 BEGIN
SELECT #iCount = COUNT(ID)
FROM tbl_new_skill_overview
WHERE Name = #sSkillName; -- No error if this line removed.
IF #iCount = 0 BEGIN
PRINT #sSkillName;
-- TODO: Insert skill
END;
FETCH curLongSkills INTO #sSkillName;
END;
CLOSE curLongSkills;
DEALLOCATE curLongSkills;
I've never liked cursors - but as a cheeky alternative, you should be able to accomplish what you want without a cursor.
insert into tbl_new_skill_overview
select //columnNames
from tbl_new_skill
WHERE Profile = 'long'
AND Parent_ID IS NULL
and name not in
(select name from tbl_new_skill)
The problem was stupidity.
The Name column in tbl_new_skill_overview was mistakenly put in as an INT, not a VARCHAR.
Thanks to all who responded, particularly bobs for asking me to show the database structure, at which point I realized the mistake.
That's a strange occurrence for sure. I have no idea what's causing it, but to get around it, perhaps you could do something like this:
if not exists (select * from tbl_new_skill_overview where Name = #sSkillName) begin
print #sSkillName;
-- TODO: Insert skill
end
That is assuming you don't use #iCount for anything else later.

Optimize for speed a simple stored procedure

In SQL 2008 I've this easy-but-bad-write sp that works:
ALTER PROCEDURE [dbo].[paActualizaCapacidadesDeZonas]
AS
BEGIN
SET NOCOUNT ON;
DECLARE #IdArticulo AS INT
DECLARE #ZonaAct AS INT
DECLARE #Suma AS INT
UPDATE CapacidadesZonas SET Ocupado=0
DECLARE csrSumas CURSOR FOR
SELECT AT.IdArticulo, T.NumZona, SUM(AT.Cantidad)
FROM ArticulosTickets AT
INNER JOIN Tickets T ON AT.IdTicket = T.IdTicket
GROUP BY AT.IdArticulo, T.NumZona
OPEN csrSumas
FETCH NEXT FROM csrSumas INTO #IdArticulo, #ZonaAct, #Suma
WHILE ##FETCH_STATUS = 0
BEGIN
UPDATE CapacidadesZonas SET Ocupado = #Suma
WHERE NumZona = #ZonaAct AND IdArticulo = #IdArticulo
FETCH NEXT FROM csrSumas INTO #IdArticulo, #ZonaAct, #Suma
END
CLOSE csrSumas
DEALLOCATE csrSumas
END
I know: I must avoid cursors, so I'm pretty sure that it can be done in a much proper way.
I've tried with a single Update query:
UPDATE CapacidadesZonas SET Ocupado =
(SELECT SUM(AT.Cantidad)
FROM ArticulosTickets AT
INNER JOIN Tickets T ON AT.IdTicket = T.IdTicket
GROUP BY AT.IdArticulo, T.NumZona)
But this is really wrong, because the select returns more than one row.
I'm feeling bad with this, because it is supposed must be easy for me, but I can't find the equivalent query.
Any suggestions?
Thanks in advance.
There are many different solutions to this problem-- see this article for a few options. Here's one way: use a derived table.
UPDATE CapacidadesZonas SET Ocupado=0 WHERE Ocupado <> 0;
UPDATE CapacidadesZonas
SET Ocupado = SUM(s.Cantidad)
FROM CapacidadesZonas C INNER JOIN
(
SELECT T.NumZona, AT.IdArticulo, SUM(AT.Cantidad) as Ocupado
FROM ArticulosTickets AT
INNER JOIN Tickets T ON AT.IdTicket = T.IdTicket
GROUP BY AT.IdArticulo, T.NumZona
) s ON s.NumZona = C.NumZona AND s.IdArticulo = C.IdArticulo;
Caveats:
are you expecting that the CapacidadesZonas table is available to a live application while the update is happening? If so you may have a locking or perf issue since SQL will may lock the whole table for the update of every row. If this is the case, consider doing your update in batches (e.g. of 1,000 rows each). UPDATE TOP makes batching easy.
sometimes SQL picks a suboptimal plan for queries like this. it may be faster to load a temp table (like in astander's solution above, but using a temp table instead of a table var) than to try to do the update as a single query. If you do this, remember to make sure there's an index on (IDArticulo, NumZona) on the the temp table before you do your update.
Try:
UPDATE cz
SET Ocupado = SUM(AT.Cantidad)
FROM CapacidadesZonas as cz
INNER JOIN ArticulosTickets AT ON cz.numZona = at.numZona and cz.IDArticulo = at.IDArticulo
INNER JOIN Tickets T ON AT.IdTicket = T.IdTicket
GROUP BY AT.IdArticulo, T.NumZona

Using SELECT resultset to run UPDATE query with MySQL Stored Procedures

I'm trying to understand MySQL Stored Procedures, I want to check if a users login credentials are valid and if so, update the users online status:
-- DROP PROCEDURE IF EXISTS checkUser;
DELIMITER //
CREATE PROCEDURE checkUser(IN in_email VARCHAR(80), IN in_password VARCHAR(50))
BEGIN
SELECT id, name FROM users WHERE email = in_email AND password = in_password LIMIT 1;
-- If result is 1, UPDATE users SET online = 1 WHERE id = "result_id";
END //
DELIMITER ;
How Can I make this if-statement based on the resultsets number of rows == 1 or id IS NOT NULL?
DELIMITER //
CREATE PROCEDURE checkUser(IN in_email VARCHAR(80), IN in_password VARCHAR(50))
BEGIN
DECLARE tempId INT DEFAULT 0;
DECLARE tempName VARCHAR(50) DEFAULT NULL;
DECLARE done INT DEFAULT 0;
DECLARE cur CURSOR FOR
SELECT id, name FROM users WHERE email = in_email AND password = in_password;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN cur;
REPEAT
FETCH cur INTO tempId, tempName;
UPDATE users SET online = 1 WHERE id = tempId;
UNTIL done = 1 END REPEAT;
CLOSE cur;
SELECT tempName;
END //
DELIMITER ;
NB: I have not tested this. It's possible that MySQL doesn't like UPDATE against a table it currently has a cursor open for.
PS: You should reconsider how you're storing passwords.
Re comment about RETURN vs. OUT vs. result set:
RETURN is used only in stored functions, not stored procedures. Stored functions are used when you want to call the routine within another SQL expression.
SELECT LCASE( checkUserFunc(?, ?) );
You can use an OUT parameter, but you have to declare a user variable first to pass as that parameter. And then you have to select that user variable to get its value anyway.
SET #outparam = null;
CALL checkUser(?, ?, #outparam);
SELECT #outparam;
When returning result sets from a stored procedure, it's easiest to use a SELECT query.
Use:
UPDATE USERS
SET online = 1
WHERE EXISTS(SELECT NULL
FROM USERS t
WHERE t.email = IN_EMAIL
AND t.password = IN_PASSWORD
AND t.id = id)
AND id = 'result_id'
Why do you have LIMIT 1 on your SELECT? Do you really expect an email and password to be in the db more than once?
You could try an if statement if you have an result which returns 1
i looked at yor code, it seems nothing returns a true so you have to refactor it,
as above omg wrote thats realy true why do you have an limit 1 in your select query where only one emailadress can exisst?
something like this
update users set if(result==1,online=1,online=0) where email=emailadress

Can I use a store procedure output in an update statement in SQL Server?

I'm converting some data in SQL Server 2005. I have a table update like this:
update Invoices set Invoices.InvoiceReference = 'NewRef'
where Invoices.InvoiceReference='Unknown'
But what I'd like to plug in instead of 'NewRef' is the output from a stored procedure that uses parameters from the columns of the Invoices table. The stored procedure itself does updates to another table. Is it possible? Something like this below (which is wrong of course :)
DECLARE #Ref nvarchar(20)
update Invoices set Invoices.InvoiceReference = (
EXEC InvoiceGenerateRef
#ClientCode = Invoices.ClientCode,
#EventCode = Invoices.EventCode,
#Ref = #Ref OUTPUT
SELECT #Ref)
where Invoices.InvoiceReference='Unknown'
Do I need to use a cursor or is the syntax just wrong?
Thanks,
Chris.
I think you would be better off changing your stored procedure into either a function or a view (depending on what you actually do in the proc).
I think what you are after is to join to the resultset of a stored proc which would not work.
You are almost there, the correct way to achieve what you are looking to do would be to define an output parameter as part of your stored procedure definition.
This paramter can then be used as part of your update statement.
DECLARE #Ref nvarchar(20)
EXEC InvoiceGenerateRef
#ClientCode = N'ABC2',
#EventCode = N'X1'
#Ref = #Ref OUTPUT
update Invoices
set Invoices.InvoiceReference = #Ref
where Invoices.InvoiceReference='Unknown'
by using OPENROWSET you can query your stored procedure results just like a view:
http://blogs.technet.com/wardpond/archive/2005/08/01/the-openrowset-trick-accessing-stored-procedure-output-in-a-select-statement.aspx
so for your case this might be useful.
1) You could change InvoiceGenerateRef so that it could optionally save the generated Ref into InvoiceReference field. Presumably you would also have to provide parameters to define the selection
2) You could us e cursor to step round each row in
SELECT ...
FROM Invoices
WHERE Invoices.InvoiceReference='Unknown'
and pass the details to InvoiceGenerateRef and then update the row. This is bad IMHO and will be slow (Your best bet is a set-based solution)
3) You could select the appropriate Invoices.ID's into a temporary table which would be in scope for the InvoiceGenerateRef so that it could iterate that (i.e. the choice of WHICH rows to update is external to the SProc, but the SProc does the actual updating)
CREATE TABLE #TEMP
(
T_ID int NOT NULL
)
INSERT INTO #TEMP (T_ID)
SELECT ID
FROM Invoices
WHERE Invoices.InvoiceReference='Unknown'
EXEC InvoiceGenerateRef #ACTION='UpdateFromTemporaryTable'
4) You could change InvoiceGenerateRef to a function that performed the same task:
UPDATE U
SET U.InvoiceReference =
dbo.MyInvoiceGenerateRefFunction(U.ClientCode, U.EventCode)
FROM Invoices AS U
WHERE U.InvoiceReference='Unknown'
MyInvoiceGenerateRefFunction would have to be deterministic (I think!)
This would be my preferred choice