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

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.

Related

SQL / mariaDB: IF does not work due to collation?

let me preface my question by saying, that my teacher stared at my code for 2h and could only guess that it might be the collation.
Here's the problem: for an exercise we were asked to create a movie database and then write several procedures. One procedure ist supposed to do the following:
- take the id of a movie (as a parameter), the director's last and first name
and then test wether the director already is in the table. If the answer is no, the name is added to the table. Either way, the procedure then adds the movieID and the ID of the director to a second table. Here's my code:
delimiter //
create or replace procedure p_director
( par_movieID integer, par_firstName varchar(50),par_lastName varchar(100))
BEGIN
DECLARE var_id INTEGER;
DECLARE var_firstName varchar(100);
DECLARE var_lastName varchar(100);
DECLARE var_control integer;
DECLARE exit handler for SQLEXCEPTION
Begin
rollback;
End;
Start TRANSACTION;
set var_firstName = par_firstName;
set var_lastName = par_lastName;
SELECT directorID FROM director
where lastName = var_lastName AND firstName = var_firstName
INTO var_control;
select var_control as debug;
IF (var_control IS NULL) THEN
select max(directorID)+1 from director into var_id;
insert into director ( directorid, firstName, lastName)
values (var_id, par_firstName, par_lastName);
insert into moviedirector (movieID, directorID) values
( par_movieID ,var_id) ;
ELSE
insert into moviedirector (movieID, directorID)
values ( par_movieID ,var_control);
END IF;
END//
delimiter ;
As you can see I added the line "select var_control as debug;". I did this to get the value of the variable displayed on the screen. Anyway. This code should run fine. I know this, because I wrote a routine that did the exact same thing for a homework assignment. And for that database it works.
However if I run this procedure unter MariaDB, it doe not work. Despite no syntax error, the statements between else and end if are never reached and, even stranger, the "select var_control as debug;" statement genereates no output on the screen, not even null!
As I said, my teacher stared at it for a solid 2 hours, finally he found the only difference between the code in my homework assignement and this exercise: the collation and caracterset of the database: For my homework, I used the collation latin1_swedish_ci, while at school we use utf8_general_ci.
Could this be the reason? Can the collation really have such a profound Impact? Has anyone of you ever run into thsi kind of problem?

Prevent column change without the other

No-one should be allowed to update the customer address column unless the postcode column is also updated. If an attempt is made to update one without the other, then a trigger will fire and the user will be shown an error message.
Any help on how I can do this in Microsoft SQL Server 2012 will be appreciated!
You can use below logic
CREATE TRIGGER [dbo].AU_MyTrigger ON [dbo].MyTable
FOR UPDATE
AS
BEGIN
declare #bu_addr varchar(100)
declare #bu_zip varchar(100)
declare #au_addr varchar(100)
declare #au_zip varchar(100)
select #bu_addr = addr, #bu_zip = zip from DELETED
select #au_addr = addr, #ay_zip = zip from INSERTED
if (#bu_addr <> #au_addr) and (#bu_zip = #au_zip)
BEGIN
-- update table with old values
-- raise error
END
END
Note that if this update can happen in batch, you need to loop through each record and update their value to old and only return error at the end of trigger (outside of loop). In that case, for iterating on updated rows, you need to use CURSOR
You case might not be as easy as I explained, but this is the approach that works.

Cursor says its read only even though I declared it "for update"

I am trying to update a row inside a cursor. What I am trying to do is update a chain of records with OLD_QTY and NEW_QTY. However when I try to do my update it gives the error The cursor is READ ONLY even though I included for update of OLD_QTY, NEW_QTY in my declration. It makes no difference if I include OLD_QTY and NEW_QTY in the select statement.
declare #current_inv_guid uniqueidentifier
declare #last_inv_guid uniqueidentifier
declare #current_vid int
declare #last_vid int
--declare #current_new_qty money
declare #last_new_qty money
--declare #current_old_qty money
declare iaCursor cursor
for select INV_GUID, old_VID
--, OLD_QTY, NEW_QTY
from #IA
order by INV_GUID, old_vid, ENTRY_NUM
for update --of OLD_QTY, NEW_QTY
open iaCursor
Fetch next from iaCursor into #current_inv_guid, #current_vid --, #current_old_qty, #current_new_qty
while ##fetch_status = 0
begin
--test to see if we hit a new chain.
if(#last_inv_guid <> #current_inv_guid or #current_vid <> #last_vid)
begin
set #last_new_QTY = (select #lots.QTY_RECEIVED from #lots where #lots.INV_GUID = #current_inv_guid and LOT_VID = #current_vid)
set #last_inv_guid = #current_inv_guid
set #last_vid = #current_vid
end
--update the current link in the chain
update #ia
set OLD_QTY = #last_new_QTY,
NEW_QTY = #last_new_QTY + QTY_CHANGE,
#last_new_QTY = #last_new_QTY + QTY_CHANGE
where current of iaCursor
--get the next link
fetch next from iaCursor into #current_inv_guid, #current_vid --, #current_old_qty, #current_new_qty
end
close iaCursor
deallocate iaCursor
Putting a order by in the select made the cursor read only.
You are not explicitly saying what behaviour you want, therefore, default rules apply, according to which, the cursor may or may not be updatable, depending on the underlying query.
It's perfectly fine to use order by in an updatable cursor, but you have to be more verbose and tell SQL Server what you want in details, for instance:
declare iaCursor cursor
local
forward_only
keyset
scroll_locks
for
select INV_GUID, old_VID
from #IA
order by INV_GUID, old_vid, ENTRY_NUM
for update of OLD_QTY, NEW_QTY
There's an import but subtle note on the documentation page that Patrick listed:
If the query references at least one table without a unique index, the
keyset cursor is converted to a static cursor.
And of course STATIC cursors are read-only.
Besides the reason you mentioned in your answer, what you're attmepting to do runs counter to the way SQL is meant to be used. Try to update the data in sets, not by rows.
I'm not positive, as I don't know your table design, but I believe the following should work. You may get better performance out of this. In particular, I'm assuming that QTY_CHANGE is coming from #ia, although this may not be the case.
UPDATE #ia as a set (OLD_QTY, NEW_QTY) = (SELECT #lots.QTY_RECEIVED + (COUNT(b.*) * a.QTY_CHANGE),
#lots.QTY_RECEIVED + ((COUNT(b.*) + 1) * a.QTY_CHANGE)
FROM #lots
LEFT JOIN #ia as b
ON b.INV_GUID = a.INV_GUID
AND b.OLD_VID = a.OLD_VID
AND b.ENTRY_NUM < a.ENTRY_NUM
WHERE #lots.INV_GUID = a.INV_GUID
AND #lots.LOT_VID = a.OLD_VID)
WHERE EXISTS (SELECT '1'
FROM #lots
WHERE #lots.INV_GUID = a.INV_GUID
AND #lots.LOT_VID = a.OLD_VID)
EDIT:
... the previous version of the answer was written with a DB2 perspective, although it would otherwise be db-agnostic. It also had the problem of using the same value of QTY_CHANGE for every row, which is unlikely. This should be a more idiomatic SQL Server 2008 version, as well as being more likely to output the correct answer:
WITH RT AS (SELECT #IA.inv_guid, #IA.old_vid, #IA.entry_num,
COALESCE(MAX(#Lots.qty_received), 0) +
SUM(#IA.qty_change) OVER(PARTITION BY #IA.inv_guid, #IA.old_vid
ORDER BY #IA.entry_num)
AS running_total
FROM #IA
LEFT JOIN #Lots
ON #Lots.inv_guid = #IA.inv_guid
AND #Lots.lot_vid = #IA.old_vid)
UPDATE #IA
SET #IA.old_qty = RT.running_total - #IA.qty_change, #IA.new_qty = RT.running_total
FROM #IA
JOIN RT
ON RT.inv_guid = #IA.inv_guid
AND RT.old_vid = #IA.old_vid
AND RT.entry_num = #IA.entry_num
Some cursor declarations do not allow updates. The documentation gives a hint in the following remark:
If the SELECT statement does not support updates (insufficient permissions, accessing remote tables that do not support updates, and
so on), the cursor is READ_ONLY.
I ran into the same issue when trying to join the "inserted" object of a trigger in the select statement of the cursor declaration.
Use the DYNAMIC clause, found in documentation.
Defines a cursor that reflects all data changes made to the rows in its result set as you scroll around the cursor. The data values, order, and membership of the rows can change on each fetch.

MySQL stored procedure - Problem outputting values

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..

Is my stored procedure executing out of order?

Brief history:
I'm writing a stored procedure to support a legacy reporting system (using SQL Server Reporting Services 2000) on a legacy web application.
In keeping with the original implementation style, each report has a dedicated stored procedure in the database that performs all the querying necessary to return a "final" dataset that can be rendered simply by the report server.
Due to the business requirements of this report, the returned dataset has an unknown number of columns (it depends on the user who executes the report, but may have 4-30 columns).
Throughout the stored procedure, I keep a column UserID to track the user's ID to perform additional querying. At the end, however, I do something like this:
UPDATE #result
SET Name = ppl.LastName + ', ' + ppl.FirstName
FROM #result r
LEFT JOIN Users u ON u.id = r.userID
LEFT JOIN People ppl ON ppl.id = u.PersonID
ALTER TABLE #result
DROP COLUMN [UserID]
SELECT * FROM #result r ORDER BY Name
Effectively I set the Name varchar column (that was previously left NULL while I was performing some pivot logic) to the desired name format in plain text.
When finished, I want to drop the UserID column as the report user shouldn't see this.
Finally, the data set returned has one column for the username, and an arbitrary number of INT columns with performance totals. For this reason, I can't simply exclude the UserID column since SQL doesn't support "SELECT * EXCEPT [UserID]" or the like.
With this known (any style pointers are appreciated but not central to this problem), here's the problem:
When I execute this stored procedure, I get an execution error:
Invalid column name 'userID'.
However, if I comment out my DROP COLUMN statement and retain the UserID, the stored procedure performs correctly.
What's going on? It certainly looks like the statements are executing out of order and it's dropping the column before I can use it to set the name strings!
[Edit 1]
I defined UserID previously (the whole stored procedure is about 200 lies of mostly irrelevant logic, so I'll paste snippets:
CREATE TABLE #result ([Name] NVARCHAR(256), [UserID] INT);
Case sensitivity isn't the problem but did point me to the right line - there was one place in which I had userID instead of UserID. Now that I fixed the case, the error message complains about UserID.
My "broken" stored procedure also works properly in SQL Server 2008 - this is either a 2000 bug or I'm severely misunderstanding how SQL Server used to work.
Thanks everyone for chiming in!
For anyone searching this in the future, I've added an extremely crude workaround to be 2000-compatible until we update our production version:
DECLARE #workaroundTableName NVARCHAR(256), #workaroundQuery NVARCHAR(2000)
SET #workaroundQuery = 'SELECT [Name]';
DECLARE cur_workaround CURSOR FOR
SELECT COLUMN_NAME FROM [tempdb].INFORMATION_SCHEMA.Columns WHERE TABLE_NAME LIKE '#result%' AND COLUMN_NAME <> 'UserID'
OPEN cur_workaround;
FETCH NEXT FROM cur_workaround INTO #workaroundTableName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #workaroundQuery = #workaroundQuery + ',[' + #workaroundTableName + ']'
FETCH NEXT FROM cur_workaround INTO #workaroundTableName
END
CLOSE cur_workaround;
DEALLOCATE cur_workaround;
SET #workaroundQuery = #workaroundQuery + ' FROM #result ORDER BY Name ASC'
EXEC(#workaroundQuery);
Thanks everyone!
A much easier solution would be to not drop the column, but don't return it in the final select.
There are all sorts of reasons why you shouldn't be returning select * from your procedure anyway.
EDIT: I see now that you have to do it this way because of an unknown number of columns.
Based on the error message, is the database case sensitive, and so there's a difference between userID and UserID?
This works for me:
CREATE TABLE #temp_t
(
myInt int,
myUser varchar(100)
)
INSERT INTO #temp_t(myInt, myUser) VALUES(1, 'Jon1')
INSERT INTO #temp_t(myInt, myUser) VALUES(2, 'Jon2')
INSERT INTO #temp_t(myInt, myUser) VALUES(3, 'Jon3')
INSERT INTO #temp_t(myInt, myUser) VALUES(4, 'Jon4')
ALTER TABLE #temp_t
DROP Column myUser
SELECT * FROM #temp_t
DROP TABLE #temp_t
It says invalid column for you. Did you check the spelling and ensure there even exists that column in your temp table.
You might try wrapping everything preceding the DROP COLUMN in a BEGIN...COMMIT transaction.
At compile time, SQL Server is probably expanding the * into the full list of columns. Thus, at run time, SQL Server executes "SELECT UserID, Name, LastName, FirstName, ..." instead of "SELECT *". Dynamically assembling the final SELECT into a string and then EXECing it at the end of the stored procedure may be the way to go.