Need help with some stored procedure - c#

The stored procedure:
ALTER PROC [Admin].[sp_Ques]
(
#QuesID bigint
)
AS
BEGIN
IF #QuesID = 0
SET #QuesID =NULL
SELECT FQ.QuesID, FQ.Ques,QuesAns
FROM Admin.Ques FQ
WHERE FQ.QuesID = Coalesce(#QuesID,QuesID)
SELECT Language FROM Admin.Language WHERE LanguageID=FQ.LanguageID
END
In the second Select statement:
SELECT Language FROM Admin.Language WHERE LanguageID=FQ.LanguageID
In this statement, I want the value of "FQ.LanguageID" from 1st select statement, so I wrote this:-
LanguageID=FQ.LanguageID
Apparently didn't work. It says "The multi-part identifier "FQ.LanguageID" could not be bound."
Do I need to pass this LanguageID to the stored procedure as a parameter and then use it as:-
SELECT Language FROM Admin.Language WHERE LanguageID=#LanguageID
How can I make this LanguageID=FQ.LanguageID work if I don't want to pass LanguageID as the second argument to the stored procedure? Is there a way?

Perhaps create a local variable to hold the LanguageID that's being retrieved. Assign a value to it during the previous SELECT. The addition of TOP 1 simply ensures that if/when you ever have multiple matches in the first query (indeed you will when #Ques is zero or null!), only one value is returned in that query, thereby allowing a single value into your variable.
DECLARE #Lang int --whatever datatype your QuesID is.
SELECT TOP 1
FQ.QuesID, FQ.Ques,QuesAns as QuesAns,
FQ.QuesAns[Answers], FQT.QuesType ,
FQ.QuesTypeID, FQ.QuesParentID, FQ.Active, FQ.AdminLanguageID
,#Lang = FQ.AdminLanguageID
FROM Admin.Ques FQ
LEFT OUTER JOIN Admin.QuesTypes FQT ON FQT.QuesTypeID=FQ.QuesTypeID
WHERE FQ.QuesID = Coalesce(#QuesID,QuesID)
SELECT TelerikLanguage FROM Admin.Language
WHERE AdminLanguageID=#Lang

The scope of FQ is limited to the first select statement.
Your options include:
Passing AdminLanguageID as a parameter as you have suggested
Retrieving AdminLanguageID in a prior statement (select #AdminLanguageID = AdminLanguageID from...)
Joining Admin.Language with Admin.Ques
Using a subquery (select ... from Admin.Language where AdminLanguageID in (select AdminLanguageID from Admin.Ques where ...)

Why not just join them into 1 select?
ALTER PROC [Admin].[sp_Ques]
(
#QuesID bigint
)
AS
BEGIN
IF #QuesID = 0
SET #QuesID =NULL
SELECT FQ.QuesID, FQ.Ques,QuesAns as QuesAns,FQ.QuesAns[Answers], FQT.QuesType ,FQ.QuesTypeID, FQ.QuesParentID, FQ.Active,FQ.AdminLanguageID, AL.TelerikLanguage
FROM Admin.Ques FQ
LEFT OUTER JOIN Admin.QuesTypes FQT ON FQT.QuesTypeID=FQ.QuesTypeID
LEFT JOIN Admin.Language AL ON AL.AdminLanguageID=FQ.AdminLanguageID
WHERE FQ.QuesID = QuesID OR #QuesID IS NULL
END

Related

SQL Table Value Parameter only works when its data is copied to temporary table

I'm passing the data through c# to sql stored procedure using dapper.
c# (using dapper):
var dt = new DataTable("dbo.TVPtype");
dt.SetTypeName("dbo.TVPtype");
dt.Columns.Add("ID", typeof(int));
dt.Rows.Add(5);
_db.Execute("mysp", param: new { TVP = dt.AsTableValuedParameter("dbo.TVPtype"), commandType: CommandType.StoredProcedure);
The bellow SQL query will not work and I don't know Why! And it removes all data from mytable.
SQL stored procedure:
CREATE PROCEDURE [dbo].[mysp] #TVP dbo.TVPtype READONLY AS
DELETE FROM [dbo].[mytable] WHERE NOT EXISTS
(
SELECT NULL FROM #TVP na
WHERE ID = na.ID
)
To solve the problem I've used a temporary table in the stored procedure like bellow, and it works well.
My solution using temporary table:
CREATE table temptb (ID int)
insert into temptb select * from #TVP
Now I use temptb instead of #TVP in the delete query:
DELETE FROM [dbo].[mytable] WHERE NOT EXISTS
(
SELECT NULL FROM temptb na
WHERE ID = na.ID
)
It works well and delete specific data (not all data) !
So what's wrong with my first query?
I can't tell you, why it's working with temporary table, but the problem comes from the DELETE statement if you change it to:
DELETE t1
FROM [dbo].[mytable] AS t1 WHERE NOT EXISTS
(
SELECT NULL FROM #TVP na
WHERE t1.ID = na.ID
)
It will work. See, that when I add alias to mytable condition becomes clear which ID's it should compare. When in your example it might compare #TVP ID with it self(that's my guess).
I want to add two more points to your query:
It's better to select something that is not NULL when you check for existence, as NULL is special in SQL(although it is working in your example). For example SELECT 1 FROM #TVP... would be more readable
Why not to do it like this:
Snippet:
DELETE FROM [dbo].[mytable] AS t1 WHERE ID NOT IN
(
SELECT na.ID FROM #TVP na
)
Qualify your column names! You think you are writing:
DELETE FROM [dbo].[mytable]
WHERE NOT EXISTS (SELECT 1
FROM #TVP na
WHERE mytable.ID = na.ID
);
But you are not. The scoping rules of SQL interpret your query as:
DELETE FROM [dbo].[mytable]
WHERE NOT EXISTS (SELECT 1
FROM #TVP na
WHERE na.ID = na.ID
);
This is rather non-sensical. The WHERE is not correlated to the outer query. It will delete either all rows (if #TVP is empty or the ID is always NULL) or no rows (any other situation).
If you qualify all column references, you will never have this problem.

Batch delete operation procedure not working

I have a stored procedure which looks like following:
alter procedure [dbo].[zsp_deleteEndedItems]
(
#ItemIDList nvarchar(max)
)
as
delete from
SearchedUserItems
WHERE EXISTS (SELECT 1 FROM dbo.SplitStringProduction(#ItemIDList,',') S1 WHERE ItemID=S1.val)
The parameter IDList is passed like following:
124125125,125125125...etc etc
And the split string function look like following:
ALTER FUNCTION [dbo].[SplitStringProduction]
(
#string nvarchar(max),
#delimiter nvarchar(5)
) RETURNS #t TABLE
(
val nvarchar(500)
)
AS
BEGIN
declare #xml xml
set #xml = N'<root><r>' + replace(#string,#delimiter,'</r><r>') + '</r></root>'
insert into #t(val)
select
r.value('.','varchar(500)') as item
from #xml.nodes('//root/r') as records(r)
RETURN
END
This is supposed to delete all items from table "SearcheduserItems" under the IDs:
124125125 and 125125125
But for some reason after I do a select to check it out:
select * from SearchedUserItems
where itemid in('124125125','125125125')
The records are still there...
What am I doing wrong here? Can someone help me out?
As mentioned in the comments, a different option would be to use a table type parameter. This makes a couple of assumptions (some commented), however, should get you on the right path:
CREATE TYPE dbo.IDList AS TABLE (ItemID int NOT NULL); --Assumed int datatype;
GO
ALTER PROC dbo.zsp_deleteEndedItems #ItemIDList dbo.IDList READONLY AS
DELETE SUI
FROM dbo.SearchedUserItems SUI
JOIN #ItemIDList IDL ON SUI.ItemID = IDL.ItemID;
GO
--Example of usage
DECLARE #ItemList dbo.IDList;
INSERT INTO #ItemList
VALUES(123456),(123457),(123458);
EXEC dbo.zsp_deleteEndedItems #ItemList;
GO
In regards to the question of an inline table value function, one such example is the below, which I quickly wrote up, that provides a tally table of the next 1000 numbers:
CREATE FUNCTION dbo.NextThousand (#Start int)
RETURNS TABLE
AS RETURN
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL)) N(N)
)
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) -1 + #Start AS I
FROM N N1 --10
CROSS JOIN N N2 --100
CROSS JOIN N N3; --1,000
GO
The important thing about an iTVF is that it has only one statement, and that is the RETURN statement. Declaring the table as a return type variable, inserting data into it, and returning that variable turns it into a multi-line TVF; which perform far slower.

EF6 fails to import stored procedure

This is a simplified version of a stored procedure
ALTER PROCEDURE [dbo].[StoredProc1]
(
#PageIndex INT = 1,
#RecordCount INT = 20,
#Gender NVARCHAR(10) = NULL
)
AS
BEGIN
SET NOCOUNT ON ;
WITH tmp1 AS
(
SELECT u.UserId, MIN(cl.ResultField) AS BestResult
FROM [Users] u
INNER JOIN Table1 tbl1 ON tbl1.UserId = u.UserId
WHERE (#Gender IS NULL OR u.Gender = #Gender)
GROUP BY u.UserID
ORDER BY BestResult
OFFSET #PageIndex * #RecordCount ROWS
FETCH NEXT #RecordCount ROWS ONLY
)
SELECT t.UserId, t.BestResult, AVG(cl.ResultField) AS Average
INTO #TmpAverage
FROM tmp1 t
INNER JOIN Table1 tbl1 ON tbl1.UserId = t.UserId
GROUP BY t.UserID, t.BestResult
ORDER BY Average
SELECT u.UserId, u.Name, u.Gender, t.BestResult, t.Average
FROM #tmpAverage t
INNER JOIN Users u on u.UserId = t.UserId
DROP TABLE #TmpAverage
END
When I use EF6 to load the stored procedure, and then go to the "Edit Function Import" dialog, no columns are displayed there. Even after I ask to Retrieve the Columns, I get the message that the SP does not return columns. When I execute the SP from SMMS, I get the expected [UserId, Name, Gender, BestResult, Average] list of records.
Any idea how can I tweak the stored procedure or EF6 to make it work?
Thanks in advance
Thanks to the comments above, the answer is that unfortunately EF6 does not cope well with TMP tables on stored procedures.
One way around is the following:
1) Comment out all temp table calls inside the Stored Procedure.
2) Change the Stored Procedure to return a Fake a result with the same exact column's names that match the expected result
3) Import the Stored Procedure into EF6
4) Double Click on the Function Imports/Stored procedure name
5) On the Edit Function Import dialog, retrieve the Columns and the create a New Complex Type that will match the fake columns
6) CTRL+Save in order to generate all the C# code
7) Re-Update the Stored Procedure by removing the fake result set and un-comment the code with the Temp tables.
That should do the job.
P.S. Special thanks for the helpers that pointed me to the right place !!!
Sometimes it works to add the following statement to the stored proc:
set fmtonly off
But still, dont leave this statement in - only use it while generating the result set

Dynamic Where Clause - Some variables not passing

I was wondering if the below scenario will work? I am having trouble with it.
I have a smart tag SQLDataSource with a query like such:
SELECT [col1], [col2], [col3] FROM [Table1] WHERE (#SubType = #SubID) ORDER BY [col1] ASC
No matter where or how I set the #SubType parameter, it does not work, yet if I change the query to WHERE [col1] = #SubID (removing the #SubType) it works fine.
Can I set a parameter as a field name to compare against like my query does?
That's not how parameters work. Parameters are not string replacement. They work with values, not database objects names (Columns, Tables, etc.).
The solution is to first assemble the SQL query with the desired columns (code behind) and then set the parameter's values.
If you want to dynamically replace the items in your WHERE clause then you will want to look at using Dynamic SQL, then you can build your SQL as a string and execute it.
Code sample from http://www.sommarskog.se/dynamic_sql.html
DECLARE #sql nvarchar(2000)
SELECT #sql = 'SELECT O.OrderID, SUM(OD.UnitPrice * OD.Quantity)
FROM dbo.Orders O
JOIN dbo.[Order Details] OD ON O.OrderID = OD.OrderID
WHERE O.OrderDate BETWEEN #from AND #to
AND EXISTS (SELECT *
FROM dbo.[Order Details] OD2
WHERE O.OrderID = OD2.OrderID
AND OD2.ProductID = #prodid)
GROUP BY O.OrderID'
EXEC sp_executesql #sql, N'#from datetime, #to datetime, #prodid int',
'19980201', '19980228', 76
Another helpful link:
Dynamic WHERE Clause

Linq distinct not working correctly

I'm having a strange problem with a linq query. I'm using LINQPad 4 to make some a query that uses regular expression using LinqToSQL as the LinqPad driver.
Here's the query that I'm trying to make :
(from match in
from s in SystemErrors
select Regex.Match(s.Description, "...")
select new
{
FamilyCode = match.Groups["FamilyCode"].Value,
ProductPrefix = match.Groups["ProductPrefix"].Value,
BillingGroup = match.Groups["BillingGroup"].Value,
Debtor = match.Groups["Debtor"].Value
}).Distinct()
As you can see I'm trying to extract data from a text description in a log table using groups. The query works, but the Distinct doesn't want to work, it returns a line for all Match.
I have read that distinct should work with anonymous type, matching each property. Even more strange is that distinct does actually do something, it orders the values alphabetically by the FamilyCode (and then by ProductPrefix, etc.).
Has someone an idea on why this isn't working?
Thanks
Here is what is displayed in the SQL tab of LinqPad :
DECLARE #p0 NVarChar(1000) = 'Big Regexp'
DECLARE #p1 NVarChar(1000) = 'FamilyCode'
DECLARE #p2 NVarChar(1000) = 'ProductPrefix'
DECLARE #p3 NVarChar(1000) = 'BillingGroup'
DECLARE #p4 NVarChar(1000) = 'Debtor'
SELECT DISTINCT [t2].[Description] AS [input], [t2].[value], [t2].[value2], [t2].[value3], [t2].[value4], [t2].[value5]
FROM (
SELECT [t1].[Description], [t1].[value], #p1 AS [value2], #p2 AS [value3], #p3 AS [value4], #p4 AS [value5]
FROM (
SELECT [t0].[Description], #p0 AS [value]
FROM [SystemError] AS [t0]
) AS [t1]
) AS [t2]
var result = from eachError in SystemErrors
let match = Regex.Match(eachError.Description, "...")
group eachError by new
{
FamilyCode = match.Groups["FamilyCode"].Value,
ProductPrefix = match.Groups["ProductPrefix"].Value,
BillingGroup = match.Groups["BillingGroup"].Value,
Debtor = match.Groups["Debtor"].Value
}
into unique
select unique.key;
When you use Distinct(), it's distinct by pointer to each object, not value because select new {} is object type not value type. Try using group by instead.
On the other hand, you can use .Distinct(IEqualityComparer<T>) overload and provided EqualityComparer for the object that you want to process.

Categories

Resources