1

I have a problem about the way of my orm tool execute procedures.

This is how i execute:

exec PostViewProc @Id='18767F6A-FF7A-47E9-AF09-6DB8A2F3B20E',@AuthorId='5455D9B9-B25A-41BD-BD2C-C9CBAE87D629'

Returns 1 row as expected..

And this is how the orm tool generates it:

exec sp_executesql 
N'EXEC [PostViewProc] @Id, @AuthorId',
N'@Id uniqueidentifier,@AuthorId uniqueidentifier',
@Id='18767F6A-FF7A-47E9-AF09-6DB8A2F3B20E',@AuthorId='5455D9B9-B25A-41BD-BD2C-C9CBAE87D629'

Returns NOTHING!

First one just works fine. But the orm-generated one, the 'sp_executesql' one NEVER works properly. If i remove @AuthorId, the orm-generated one works fine but then i add some other parameter and it blows again.

I have a few other procedures and living the same problem over and over again. Different parameters causes unexpected results, even the count of the parameters causes unexpected results. Even errors. (when using sp_executesql, the other works just soo fine)

I need some good explanation about what's going on here because there is NOTHING wrong with the procedure itself and i'm so confused right now.

nick cubric
  • 193
  • 1
  • 9
  • In what order are the parameters defined in the actual procedure? The only difference I can see is that in your query you explicitly state the parameters, whereas the dynamic sql just passes values (`EXEC proc @Param = @ParamValue` vs `EXEC proc @ParamValue`) – GarethD Feb 17 '15 at 09:37
  • @GarethD Id 1, AuthorId 8. Do i have to define all parameters and set them some values even if i don't need them (because it works that way). The procedure handles null parameters, actually all the parameters default values are null. I can execute it just like "Exec PostViewProc" without any parameters.. – nick cubric Feb 17 '15 at 09:52
  • Not sure what ORM it is, but I would say the workaround is to pass all the parameters in the correct order, passing null where appropriate. Remember that the procedure has no knowledge of the name of the parameter being passed - so although you are passing a variable called `@AuthorID` the procedure only sees it's value so there is no way of guessing that you wanted to pass this as the value for the parameter of the same name. – GarethD Feb 17 '15 at 10:00
  • It looks like i need to pass all parameters, i agree on that but gotta ask; i do pass parameters names and types when using sp_executesql don't i? Why the proc cannot match them? What does all that 4 lines of tsql code do? I don't get it. What more should i define to that proc to make it get what's happening here? :) – nick cubric Feb 17 '15 at 10:13
  • Took me a bit more than a comment to answer all those questions so had to add an answer, but hopefully it helps. – GarethD Feb 17 '15 at 10:49

3 Answers3

0

We did discuss about that situation and decided that the best solution is passing all the parameters in the exact rigth order. If the procedure handles null values for its own parameters then just set them null or any other value that suits best.

Thank you both for your time, patience and interest. @GarethD & @Mukund

nick cubric
  • 193
  • 1
  • 9
0

Okay, as identified in a comment the issue is the order of parameters. To demonstrate the issue imagine the following simple procedure

CREATE PROCEDURE dbo.TestProc @A INT = NULL, @B INT = NULL, @C INT = NULL
AS
BEGIN
    SELECT TOP 1 A = @A, B = @B, C = @C;
END
GO

The key thing to remember is that there is no mapping between variables and parameters, e.g.

DECLARE @C INT = 1;
EXECUTE dbo.TestProc @C;

Does not recognise that your variable is called @C therefore you want to assign this to the parameter of the same name. If you do not explicitly allocate values to parameters when calling your procedure then they will be allocated on a first come first served basis, so the above will return:

A   B       C
------------------
1   NULL    NULL

In fact having since your variable is nothing more than a place holder the above query is no different to just executing:

EXECUTE dbo.TestProc 1;

And you certainly would not expect the procedure to know that you intended to pass 1 as the value for @C not @A. The query your ORM is generating has a similar issue. The equivalent code with the above procedure is:

EXECUTE sp_executesql N'EXEC dbo.TestProc @A, @C', N'@A INT, @C INT', @A = 1, @C = 2;

Which returns:

A   B   C
------------
1   2   NULL

Now to answer some of your questions in the comment.

I do pass parameters names and types when using sp_executesql don't I?

Yes, you can, but it is not compulory, the following two statements will yield the same result:

EXECUTE sp_executesql 
    N'EXEC dbo.TestProc @A = @A, @B = @B, @C = @C', 
    N'@A INT, @B INT, @C INT', 
    @A = 1, @B = 2, @C = 3; 

EXECUTE sp_executesql 
    N'EXEC dbo.TestProc @A, @B, @C', 
    N'@A INT, @B INT, @C INT', 
    1, 2, 3; 

If you only want to pass certain values then it is advisable, but not compulsory to use named parameters. The difference is that if you don't use the parameter names then you must provide the parameters in the correct order:

EXECUTE sp_executesql 
    N'EXEC dbo.TestProc @A = @A, @C = @C', 
    N'@A INT, @C INT', 
    @A = 1, @C = 3; 

EXECUTE sp_executesql 
    N'EXEC dbo.TestProc @A, @B, @C', 
    N'@A INT, @B INT, @C INT', 
    1, NULL, 3; 

Why the proc cannot match them?

As explaned above, the procedure has no knowledge of your variable name, just its value, so the following two statements are equivalent:

DECLARE @C INT = 1;
EXECUTE dbo.TestProc @c;

EXECUTE dbo.TestProc 1;

If you examine the execution plan XML for the former query, you can confirm this:

<ParameterList>
    <ColumnReference Column="@C" ParameterCompiledValue="NULL" ParameterRuntimeValue="NULL" />
    <ColumnReference Column="@B" ParameterCompiledValue="NULL" ParameterRuntimeValue="NULL" />
    <ColumnReference Column="@A" ParameterCompiledValue="NULL" ParameterRuntimeValue="(1)" />
</ParameterList>

Any reference to the name of the variable passed to the procedure is lost. Furthermore even if it were not lost there is no reason why it should match up, what would you have it do with mis-matches, e.g.

EXECUTE dbo.TestProc @D, @A, 2;

@A matches a paramter name, but @D does not and neither does the constant 2. There is no sensible way of sorting this out, and AFIK there is no language that infers parameters from the names of variables passed.

What does all that 4 lines of tsql code do?

1.  exec sp_executesql 
2.  N'EXEC [PostViewProc] @Id, @AuthorId',
3.  N'@Id uniqueidentifier,@AuthorId uniqueidentifier',
4.  @Id='18767F6A-FF7A-47E9-AF09-6DB8A2F3B20E',@AuthorId='5455D9B9-B25A-41BD-BD2C-C9CBAE87D629'
  • Line 1 is just the invokation of the procedure sp_executesql.
  • Line 2 is the statement parameter - this is the sql to be executed.
  • Line 3 is the @params parameter, this contains the definition of all the parameters used in the statement.
  • Line 4 is the values for the parameters used in @params, unless explicitly allocated (e.g. @A = 1, @B = 2) these are allocated in the same order as the parameters are defined

What more should i define to that proc to make it get what's happening here?

I don't know what ORM you are using so I am not sure how to force it to generate the right code, it should be generating:

EXEC sp_executesql 
N'EXEC [PostViewProc] @Id = @Id, @AuthorID = @AuthorId',
N'@Id uniqueidentifier,@AuthorId uniqueidentifier',
@Id='18767F6A-FF7A-47E9-AF09-6DB8A2F3B20E',@AuthorId='5455D9B9-B25A-41BD-BD2C-C9CBAE87D629';

The difference being the second line has changed from

N'EXEC [PostViewProc] @Id, @AuthorId',

to

N'EXEC [PostViewProc] @Id = @Id, @AuthorID = @AuthorId',

If you cannot enforce using named parameters then you will have to pass all values in the correct order, including NULL values.

GarethD
  • 68,045
  • 10
  • 83
  • 123
-1

Try following script,

exec sp_executesql '18767F6A-FF7A-47E9-AF09-6DB8A2F3B20E','5455D9B9-B25A-41BD-BD2C-C9CBAE87D629'
Mukund Thakkar
  • 1,225
  • 14
  • 19
  • Msg 214, Level 16, State 2, Procedure sp_executesql, Line 1 Procedure expects parameter '@statement' of type 'ntext/nchar/nvarchar'. – nick cubric Feb 17 '15 at 09:38
  • You can modify the exec statement if there are more parameter in your stored procedure. – Mukund Thakkar Feb 17 '15 at 09:42
  • statement is not a parameter of the procedure. There are more parameters then those two but proc handles null parameters, actually default values are null for each parameter. Could you explain 'modify', i mean how should i? – nick cubric Feb 17 '15 at 09:53
  • This procedure in my northwind database, CREATE PROCEDURE CustOrderHist '@CustomerID nchar(5) AS SELECT ProductName, Total=SUM(Quantity) FROM Products P, [Order Details] OD, Orders O, Customers C WHERE C.CustomerID = @CustomerID AND C.CustomerID = O.CustomerID AND O.OrderID = OD.OrderID AND OD.ProductID = P.ProductID GROUP BY ProductName I can execute this sp as exec dbo.CustOrderHist ALFKI – Mukund Thakkar Feb 17 '15 at 09:58
  • Oh ok now i got it. And no it's not the situation here, i have 11 parameters and some of them will be null. For example, on page x i need 1st and 5th params, on page y i need 3rd and 8th param, the rest is null. – nick cubric Feb 17 '15 at 10:06
  • If the parameter is null then you can skip that parameter value like, exec sp_executesql '18767F6A-FF7A-47E9-AF09-6DB8A2F3B20E','5455D9B9-B25A-41BD-BD2C-C9CBAE87D629','','','','' – Mukund Thakkar Feb 17 '15 at 10:16
  • yeah, it looks like that's the way of solving this, i agree on that. Thank you for your time and interest. I will pass all the parameters. – nick cubric Feb 17 '15 at 10:19