Not getting any results in stored procedure - sql

For some reason, every time I run exec
communications_getCode #telCode='MX'
I get empty results. I know I am missing something because if I run
Select * from tbl_telCode where code = 'MX'
I get results (1 to be precise). But if I try it with the procedure, I get blank results
CREATE PROCEDURE dbo.communications_getCode
#telcode varchar
AS
SELECT
id, code, detail
FROM
tbl_telCode
WHERE
[code] = #telcode;
I do not know what am I missing.

The varchar in the declaration defaults to varchar(1). When you pass a longer string, it gets truncated to one character.
In SQL Server, always user a length with string definitions:
CREATE PROCEDURE dbo.communications_getCode (
#telcode varchar(255)
) AS
BEGIN
SELECT id, code, detail
FROM tbl_telCode
WHERE [code] = #telcode;
END;
Note that the body of the stored procedure is wrapped in a BEGIN/END. I find this to be a useful practice.
Also, there is no reason to define a stored procedure for this. In my opinion, this would be better defined as a function.

Related

Cannot create new procedure in SQL manager lite

I am working with SQL manager lite for Interbase/Firebird application. I have downloaded firebird database, successfully connected to that database and its host, but now I want to create procedure.
I couldn't done it via tutorials, so I decided to just click New->Procedure and do that automatically. But doing this way I still have errors.
My code what I have tried without clicking New->Procedure:
CREATE PROCEDURE MyProc
AS
SELECT M_DOKUMENTY.NDZIEN FROM M_DOKUMENTY WHERE M_DOKUMENTY.SRODZAJ = '1234'
GO;
The code which was generated using New->Procedure wizard:
CREATE PROCEDURE SHOW_ALL
AS
BEGIN
/* Procedure body */
SELECT
M_DOKUMENTY.NDZIEN,
M_DOKUMENTY.CKIERUNEK,
M_DOKUMENTY.CMEDIUM FROM M_DOKUMENTY WHERE M_DOKUMENTY.SRODZAJ = '1234'
SUSPEND;
END;
But when I am clicking that lightning icon (compile) it complains about error:
Dynamic SQL Error.
SQL error code = -104.
Token unknown - line 9, column 3.
SUSPEND.
How to fix that?
Screenshot of error in SQL Manager lite
The problem is that your syntax is wrong. You need to define the output parameters, and you need to use either select ... into <list of variables> to select a single row, or for select ... into <list of variables> do to loop over multiple rows.
Your stored procedure should be something like:
CREATE PROCEDURE SHOW_ALL
RETURNS (NDZIEN varchar(50), CKIERUNEK varchar(50), CMEDIUM varchar(50))
AS
BEGIN
/* Procedure body */
for SELECT
M_DOKUMENTY.NDZIEN,
M_DOKUMENTY.CKIERUNEK,
M_DOKUMENTY.CMEDIUM
FROM M_DOKUMENTY
WHERE M_DOKUMENTY.SRODZAJ = '1234'
into :NDZIEN, :CKIERUNEK, :CMEDIUM
do
SUSPEND;
END
If your select only produces a single row, then you could also consider using
CREATE PROCEDURE SHOW_ALL
RETURNS (NDZIEN varchar(50), CKIERUNEK varchar(50), CMEDIUM varchar(50))
AS
BEGIN
/* Procedure body */
SELECT
M_DOKUMENTY.NDZIEN,
M_DOKUMENTY.CKIERUNEK,
M_DOKUMENTY.CMEDIUM
FROM M_DOKUMENTY
WHERE M_DOKUMENTY.SRODZAJ = '1234'
into :NDZIEN, :CKIERUNEK, :CMEDIUM;
SUSPEND;
END
Notice the ; after the into clause. In this case you could also leave out the SUSPEND;. That will make the stored procedure executable instead of selectable. Depending on how you want to use it, that could be a better choice.
See the Firebird documentation on created stored procedures and its procedural SQL language for more information.

Pass declare table variable to another stored procedure

I am sure the answer is NO, but I'll ask the expert anyway ;)
I've declared a table variable in my stored procedure.
DECLARE #OrderMapIds TABLE
(
OrderId INT NOT NULL,
NewOrderId INT NOT NULL
);
INSERT INTO #OrderMapIds (OrderId, NewOrderId)
SELECT [OrderId], [OrderId] FROM [tblOrder]
...
...
...
EXEC [AS.uspOrder_MoveOrder] #OrderMapIds = #OrderMapIds; --I need to move order ids based on the mapped id
I need to pass #OrderMapIds to [AS.uspOrder_MoveOrder]. The question is how?
CREATE PROCEDURE [AS.uspOrderItem_CopyRecord]
(
#OrderMapIds AS TABLE -- This thrown error
)
AS
BEGIN
...
...
...
END;
Now, I can accomplish this problem using Table-Valued Parameter (TVP). But if I could pass it without TVP, then it will be better (so I don't have to create TVP for small stuff).
Now, after looking at Google, I am sure the answer is NO (ie. I need to create TVP to accomplish task above). But I thought to ask the question in hope I might have missed something.
Any help is greatly appreciated.
Thanks
You are correct. The answer is indeed No.
In SQL Server, the only way to pass a table to a stored procedure is using a user defined table type.
You do have some confusion in the terms, though.
TVP is the parameter itself - so even if you could just pass any table variable a stored procedure - it would still be a Table Valued Parameter.
What you want to avoid (but can't) is a User Defined Table Type.
If this was allowed, you would end up with a stored procedure that takes in a table valued parameter with an unknown structure - And this could lead to errors, extremely cumbersome code, and worst of all - silently using the wrong data.
An interesting alternative, especially when the table structure is variable/dependent will be to convert data as JSON/XML ( data type for parameter will be NVARCHAR).
Way to go in your example will be
DECLARE #OrderMapIds NVARCHAR(MAX)=
(
SELECT [OrderId], [OrderId] FROM [tblOrder] FOR JSON PATH
);
...
...
...
EXEC [AS.uspOrder_MoveOrder] #OrderMapIds = #OrderMapIds;

Get a specific column from a returned result of SP?

I have a stored procedure that returns a result, let's say it return rows of products. But each product status is not in our hand(can't get it). Our DBA just gave us another stored procedure to get the status of a product. We need to get individual product status by calling their SP. Let's say we have Product table,
CREATE TABLE PRODUCTS
(
ID INT,
Name NVARCHAR(100)
)
CREATE PROCEDURE GetProducts
AS
BEGIN
INSERT INTO #TEMPTABLE
SELECT * FROM PRODUCTS; -- Yes Too Much simplified
-- Create cursor and set additional status in #TEMPTABLE
END;
EXEC GetStatus #ProductId; -- SP That need to get status
The problem is that GetStatus is only way to get the status and this sp sometimes return 2 columns, sometimes 4 and sometimes 8. The return columns will always include Status column for sure.
If columns names is fixed then there is no problem. Is there is a way to create dynamic table at the time of executing SP.
Tried this but not working,
WITH DynamicTable AS
(
EXEC GetStatus
)
The answer to your question is no. There is no good way to get the value of a specific column returned by a stored procedure that can return a dynamic set of columns.
I say no "good" way, because of course there's a WAY. You can write an INSERT EXEC statement for every possible set of columns that the procedure can return and wrap each one in a TRY..CATCH block. If the first one errors, try the next one. As soon as you hit one that doesn't error, get the Status from it, and skip the rest.
That's the answer to your question. The solution to your problem, however, is to replace the GetStatus stored procedure with a Table-valued function. Then you can select from it, join to it, etc. I think the function would have to always return a consistent number of columns, but that would be better anyway, and the columns that aren't needed in a specific case could just be left empty or NULL.

SELECT Query selecting values based on a value in another table

I have 2 tables
Account(AccountId, Encoding)
DeviceAccountMap(AccountId, DeviceId)
Now I need to fetch the devices from the DeviceAccountMap. I pass a list of AccountId to a stored procedure and while fetching the DeviceId from the DeviceAccountMap table I need to compare the Encoding value for each account with a particular value.
Which is the easy way to do this? I am totally lost.
The select clause in the stored procedure will look something like this:
DECLARE #Accounts [usp].[Array]
and [usp].[Array] is defined as below
CREATE TYPE [usp].[Array] AS TABLE
(
Value VARCHAR(36) NULL
)
SELECT
DeviceId,
AccountEncoding = A.Encoding
FROM
usp.DeviceControllerAccountMap DCAM
INNER JOIN
usp.Account A ON (DCAM.AccountId = A.AccountId)
WHERE
DCAM.AccountId IN (SELECT Value From #AccountIds)
AND DCAM.IsShared = 1
AND AccountEncoding LIKE A.Encoding + '.%'
In other words I need to fetch the encoding value for each account and use that in this where clause.
So you can look up information on Table-Valued Parameters (TVPs) in T-SQL.
Here is an article by Erland Sommarskog.
You can refer to this StackOverflow answer to see an example of C# code calling a stored procedure that uses a TVP. I believe TVPs require SQL Server 2008 or higher.
TVPs, as far as I understand, provide a way to make your own data type in sql server that gets treated as if it was a table. You're doing this when you declare your Array type and then when you use the #AccountIds in your stored procedure's select statement.
CREATE TYPE [usp].[Array] AS TABLE -- maybe choose a more descriptive name than 'Array'
(
Value VARCHAR(36) NULL -- choose a more descriptive name than 'Value'
)
CREATE PROCEDURE [usp].[your_procedure_name]
#AccountIds [usp].[Array] READONLY -- use TVP as a parameter
AS
SELECT …
It is not clear form your question details whether you also mean to have a parameter in the stored procedure for the Encoding. It seems like you're looking for accounts whose Encodings start with a period '.'.
So first, create your type, like you're doing.
Then create your stored procedure.
Then test your stored procedure, something like this:
DECLARE #mylist Array -- make TVP sample data
INSERT #mylist(Value) VALUES(1),(11),(27),(123) -- insert some values
exec your_procedure_name #mylist -- run stored procedure
The following line is completely unnecessary. The JOIN to Account does this filter for you.
DCAM.AccountId IN (SELECT Value From #AccountIds)
Or am I missing something?

Why does this SQL stored procedure require that a temp table be created for it to work (return results)?

IBM Informix Dynamic Server Version 11.50.FC6
I was working on a small stored procedure that would take name fields from a table and parse them into "user names" with a maximum of 8 chars.
This is the code I was trying:
CREATE PROCEDURE build_jics_user (pid INT)
RETURNING CHAR(8) AS username;
SELECT LOWER((SUBSTR(firstname,0,1))||(SUBSTR(lastname,0,7))) username
FROM id_rec
WHERE id = pid;
END PROCEDURE;
The error returned when executed is:
659: INTO TEMP table required for SELECT statement.
Error in line 5
Near character position 15
I don't understand what the point of summoning a temporary table is, and I also couldn't find any similarly simple examples online that would work without error.
Does anyone know what I'm missing?
What you want to say is this:
CREATE PROCEDURE build_jics_user (pid INT)
RETURNING CHAR(8);
DEFINE username CHAR(8);
SELECT LOWER((SUBSTR(firstname,0,1))||(SUBSTR(lastname,0,7))) INTO username
FROM id_rec
WHERE id = pid;
RETURN username;
END PROCEDURE;
... and execute it like this:
EXECUTE PROCEDURE build_jics_user(42);
UPDATE
If the purpose of this is to be a function, where it's required inside some other SQL, then you might do the following:
CREATE FUNCTION jics_user(fname VARCHAR(255), lname VARCHAR(255))
RETURNING CHAR(8);
RETURN LOWER(SUBSTR(fname,0,1) || SUBSTR(lname,0,7));
END FUNCTION;
... and execute it like this:
SELECT id, firstname, lastname, jics_user(firstname, lastname) AS jics_user, ...
FROM id_rec;
There's no real technical difference between a PROCEDURE and a FUNCTION, it's more an assertion as to how it's used.
This seems to be per design (which must be accounting for the absence of the 'similarly simple examples online'). Apparently, whatever data you are pulling with a SELECT statement in a stored procedure, you cannot return them directly. You should store them either in a temporary table or in variables for later use.
It is likely that your SELECT statement should look like this
SELECT LOWER((SUBSTR(firstname,0,1))||(SUBSTR(lastname,0,7))) INTO username
FROM id_rec
WHERE id = pid;