I'm trying to add the following sproc to Entity Framework. After adding this via "Update from Model" the Model Browser shows this sproc in the "Function Imports" and "Stored Procedures/Functions" of the model. Using the "Edit" from Function Imports dialog I can not "Get Column Information" and unsuccessfully determine a Return type for the collection.
The output from the sproc is a temporary table but I do define the columns being returned. Is this sproc the problem? Am I missing a step in setting up the EF?
ALTER PROCEDURE [dbo].[addrApproxSP]
-- Add the parameters for the stored procedure here
#frontage bigint = 0,
#housedir varchar(1) = 0,
#streetnum bigint = 0,
#streetdir varchar(1) = 0,
#distance bigint = 0
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Set Variables
DECLARE #lowfront bigint,
#highfront bigint,
#lowstreet bigint,
#highstreet bigint,
#currname varchar(25),
#streetcur varchar(25),
#whereclause varchar(40),
#fixstreet varchar(25),
#pos int,
#piece varchar(500)
--Set variables to proper values in range
Set #lowfront = #frontage - #distance
set #highfront = #frontage + #distance
set #lowstreet = #streetnum - #distance
set #highstreet = #streetnum + #distance
-- Process for Street Names that are Numeric Values
-- Create Temp Table
CREATE TABLE #StreetNames
(streetname varchar(25))
-- SELECT StreetName and put in Temp table
INSERT #Streetnames(streetname)
select distinct streetname
from ADDR_STREETCOORD
where begincoord between #lowstreet and #highstreet
and STREETDIR = #streetdir
and lowhouse > #lowfront and HIGHHOUSE < #highfront
and HOUSEDIR = #housedir
union
select distinct streetname
from ADDR_STREETCOORD
where lowhouse > #lowstreet and HIGHHOUSE < #highstreet
and HOUSEDIR = #housedir
and begincoord between #lowfront and #highfront
and STREETDIR = #streetdir
-- Check each Streetname and those that are a coordinate (ex: "1000 S") change to "1000"
CREATE TABLE #FixStreets(streetname varchar(25))
DECLARE curStreet CURSOR FOR SELECT streetname FROM #StreetNames
OPEN curStreet
FETCH NEXT FROM curStreet INTO #fixstreet
WHILE ##FETCH_STATUS = 0
BEGIN
--insert code here
INSERT #FixStreets(streetname)
--Call Function to: parse the street name
--if the first part isnumeric then insert that
--if not numeric then keep as is
select [dbo].fnParseCoordinate(#fixstreet)
FETCH NEXT FROM curStreet INTO #fixstreet
END
CLOSE curStreet
DEALLOCATE curStreet
--select * from #FixStreets
--create a temp table to store the results of each street name in a single table
-- in order to return the results as a single table
--For Each street name search its frontage range values
--loop through each streetname to get the parcels matching those streets
CREATE TABLE #AllResults(Parcel varchar(14),Prop_locat varchar(50))
DECLARE curName CURSOR FOR SELECT streetname FROM #FixStreets
OPEN curName
FETCH NEXT FROM curName INTO #streetcur
WHILE ##FETCH_STATUS = 0
BEGIN
--insert code here
INSERT #AllResults(Parcel, Prop_locat)
select parcel_id,prop_locat
from ADDR_ParcelsWWW
where StreetName = #streetcur
and predir = #housedir
and housefrom between #lowfront and #highfront
union
select parcel_id,LOCATOR_ADDRESS
from ADDR_MASTERADDRESS
where StreetName = #streetcur
and predir = #housedir
and housefrom between #lowfront and #highfront
FETCH NEXT FROM curName INTO #streetcur
END
CLOSE curName
DEALLOCATE curName
--Select the results of all the tables
select Parcel, prop_locat from #AllResults
END
When EF polls your SP, it does this first: SET FMTONLY ON
This can screw things up if you use temp tables in your SP, which it looks like you're doing.
Try explicitly setting this at the beginning of your SP: SET FMTONLY OFF
That should allow EF to detect your columns.
Related
I'm inserting DataTable in Database using StoredProcedure but the issue is, its inserting twice the actual number of entries of DataTable to be inserted, the procedure is below, kindly guide me, if I'm using wrong approach, why its duplicating the rows? The return which is required is working fine.
Thanks In Advance
ALTER PROCEDURE [dbo].[proc_InsertStore_Recvry]
(#dt_Recovery Recovery_Store READONLY)
AS
Declare #RecoveryIDs as Table (IDs int, ClientIds int)
declare #StoreID int
declare #ClientID int
declare #Arrears decimal(18, 2)
declare #NetDues decimal(18, 2)
declare #Received decimal(18, 2)
Declare #RecoveryRecID int
begin
select * into #tempTable from #dt_Recovery
declare #Count int
set #Count= (select COUNT(*) from #tempTable)
while(#Count > 0)
begin
set #Count = #Count-1
set #ClientID = (Select top 1 ClientID from #tempTable)
set #StoredID = (Select top 1 StoredID from #tempTable where ClientID=#ClientID)
set #Arrears = (Select top 1 Arrears from #tempTable where ClientID=#ClientID)
set #NetDues = (Select top 1 NDues from #tempTable where ClientID=#ClientID)
set #Received = (Select top 1 Received from #tempTable where ClientID=#ClientID)
Insert into tblRecovery (StoreID, ClientID, Arrears, NetDues, Received)
values (#StoreID,#ClientID,#Arrears,#NetDues,#Received)
select #RecoveryID = Scope_Identity()
insert into #RecoveryIDs (IDs,ClientIds) values (#RecoveryID, #ClientID )
delete from #tempTable where ClientID=#ClientID
end
Select * from #RecoveryIDs
it looks like you are using SQL Server. If yes then why are you using a while-loop to insert values into a table and return the inserted Ids?
The same can be accomplished in a far better way via the OUTPUT clause:
OUTPUT documentation
Example:
INSERT INTO tblRecovery(StoreID, ClientID, Arrears, NetDues, Received) OUTPUT INSERTED.ID, INSERTED.CLientId INTO #RecoveryIDs(IDs, ClientIds) SELECT StoredID, ClientID, Arrears, NDues, Received FROM #tempTable
Aside from that there seems to be no issue with your SQL code. So could you post the .NET code as well?
I have checked over this several times. The column count matches yet I keep getting an error saying the fetch into statement does not match the count,
CREATE PROCEDURE [dbo].[SPXMLCSA]
(
#CounterStockMaster text,
#CounterStockDetails text
)
AS
DECLARE #M0 VARCHAR(100) --EditStatus
DECLARE #M1 VARCHAR(100) --Counter_Code
DECLARE #M2 VARCHAR(100) --Counter_Name
DECLARE #M3 VARCHAR(100) --To Branch_Code
DECLARE #D1 VARCHAR(100) --Project Type
DECLARE #D2 VARCHAR(100) --drpConter.Text
DECLARE #D3 VARCHAR(100) --grdGO.Rows[i].Cells["ItemCode"].Value
DECLARE #D4 VARCHAR(100) --grdGO.Rows[i].Cells["Qty"].Value
DECLARE #D5 VARCHAR(100) --Counter_Stock_Date
DECLARE #UomCode varchar(100) -- UOM CODE
DECLARE #NoOfUnits varchar(100) -- NO OF UINTS
DECLARE #UomSrate varchar(100) -- UOM PRATE
DECLARE #UomName varchar(100) -- UOM NAME
DECLARE #UomQty varchar(100) -- UOM QUANTITY
BEGIN
DECLARE #CNTNo int
DECLARE #idoc int
DECLARE #INDate Datetime
DECLARE #Branch_Code NUMERIC(18,0)
DECLARE #ItemCode NUMERIC(18,0)
DECLARE #ItemQty NUMERIC(18,3)
DECLARE #PurRate NUMERIC(18,2)
DECLARE #SaleRate NUMERIC(18,2)
DECLARE #MRP NUMERIC(18,2)
DECLARE #PurDate DATETIME
DECLARE #Batch_No VARCHAR(50)
DECLARE #ExpiryDate DATETIME
DECLARE #MultiMRP BIT
BEGIN TRANSACTION
SET DATEFORMAT dmy
SET #MultiMRP = (Select ISNULL(Multiple_Mrp,0) from [Company])
--===================================================================================================================
--#GOMaster
--===================================================================================================================
EXEC sp_xml_preparedocument #idoc OUTPUT, #CounterStockMaster
DECLARE GINMasterCursor CURSOR FOR
SELECT * FROM OPENXML (#idoc, '/CSMASTER/ID',1)
WITH (M0 VARCHAR(100), M1 VARCHAR(100), M2 VARCHAR(100),M3 VARCHAR(100))
OPEN GINMasterCursor
FETCH NEXT FROM GINMasterCursor INTO #M0,#M1,#M2,#M3
IF #M0='T' ---Edit Mode TRUE
BEGIN --- Reversing the Item Stock for the Editing Sales START
SET #CNTNo = #M1
DECLARE GInDetailCursor CURSOR FOR
SELECT Counter_Stock_Code,Item_Code,Item_Qty,Branch_Code From [Counter Stock Details]
WHERE Counter_Stock_Code = #CNTNo AND Branch_Code=#M3
OPEN GInDetailCursor
FETCH NEXT FROM GInDetailCursor INTO #CNTNo,#ItemCode,#ItemQty,#Branch_Code
WHILE ##FETCH_STATUS=0
BEGIN
IF #MultiMRP = 0
UPDATE [ITEM MASTER] SET ITEM_BAL = ITEM_BAL - #ItemQty , Transfer_flag=2, Ascend_flag=1 WHERE Item_Code = #ItemCode and Type_Code = 0 and Branch_Code = #M3
ELSE
UPDATE [ITEM MASTER] SET ITEM_BAL = ITEM_BAL - #ItemQty , Transfer_flag=2, Ascend_flag=1 WHERE Item_Code = #ItemCode and Item_MRP = #MRP and Type_Code = 0 and Branch_Code = #M3
FETCH NEXT FROM GInDetailCursor INTO #CNTNo,#ItemCode,#ItemQty,#PurRate,#SaleRate,#MRP,#PurDate,#Branch_Code,#Batch_No,#ExpiryDate
END
CLOSE GInDetailCursor
DEALLOCATE GInDetailCursor
DELETE [Counter Stock Master] WHERE Counter_Stock_Code=#CNTNo
DELETE [Counter Stock Details] WHERE Counter_Stock_Code=#CNTNo
END --- Reversing the Item Stock for the Editing GO END
ELSE
BEGIN
SET #CNTNo = (SELECT ISNULL(MAX(Counter_Stock_Code)+1,1) FROM [Counter Stock Master] where Branch_Code = #M3)
END
INSERT INTO [Counter Stock Master]
(Counter_Stock_Code,Counter_Stock_Date,Branch_Code)
VALUES
(#CNTNo, #D5, #M3)
CLOSE GINMasterCursor
DEALLOCATE GINMasterCursor
EXEC sp_xml_removedocument #idoc
-- Create an internal representation of the XML document.
EXEC sp_xml_preparedocument #idoc OUTPUT, #CounterStockDetails
-- Execute a SELECT statement using OPENXML rowset provider.
DECLARE GInDetailsCursor CURSOR FOR
SELECT * FROM OPENXML (#idoc, '/CSDETAILS/ID',1)
WITH ( D1 VARCHAR(100), D2 VARCHAR(100), D3 VARCHAR(100), D4 VARCHAR(100))
OPEN GInDetailsCursor
FETCH NEXT FROM GInDetailsCursor INTO #D1,#D2,#D3,#D4
WHILE ##FETCH_STATUS = 0
BEGIN
IF #D1='A' or #D1='D' --For ProjectType ==> Departmental Stores
BEGIN
INSERT INTO [Counter Stock Details]
(Counter_Stock_Code,Item_Code,Item_Qty,Branch_Code)
VALUES
(#D2, #D3, #D4, #M3)
IF #MultiMRP = 0
UPDATE [ITEM MASTER] SET ITEM_BAL = ITEM_BAL + #D4 , Transfer_flag=2, Ascend_flag=1 WHERE Item_Code = #D3 and Type_Code = 0 and Branch_Code = #M3
ELSE
UPDATE [ITEM MASTER] SET ITEM_BAL = ITEM_BAL + #D4 , Transfer_flag=2, Ascend_flag=1 WHERE Item_Code = #D3 and Type_Code = 0 and Branch_Code = #M3
END
FETCH NEXT FROM GInDetailsCursor INTO #D1,#D2,#D3,#D4
END
CLOSE GInDetailsCursor
DEALLOCATE GInDetailsCursor
EXEC sp_xml_removedocument #idoc
SELECT #CNTNo
COMMIT TRANSACTION
END
GO
Error: Cursorfetch: The number of variables declared in the INTO list must match that of selected columns.
The second fetch from GInDetailCursor doesn't seem to match the cursor definition.
FETCH NEXT FROM GInDetailCursor INTO #CNTNo,#ItemCode,#ItemQty,#PurRate,#SaleRate,#MRP,#PurDate,#Branch_Code,#Batch_No,#ExpiryDate
I have a database which stores information about a library (books, authors & categories).
But I can't get my stored procedure to work for inserting data. The stored procedure itself executes fine, but when I perform a test, it simply doesn't add anything to the database. Can anyone see what I'm missing?
This is my stored procedure (for category):
USE MyLibrary
GO
IF EXISTS (SELECT 1 FROM sysobjects WHERE name = 'CategoryInsert' AND TYPE = 'P')
BEGIN
DROP PROC CategoryInsert
END
GO
CREATE PROCEDURE CategoryInsert
(
#Id int out,
#Name nvarchar(255),
#InsertedBy nvarchar(120),
#InsertedOn datetime
)
AS
DECLARE #CurrentId int
SELECT #CurrentId = Id FROM Category WHERE lower(#Name) = lower(#Name)
IF #CurrentId IS NOT NULL
BEGIN
SET #Id = -100
RETURN
END
INSERT INTO Category
(
Name,
InsertedBy,
InsertedOn
)
VALUES
(
#Name,
#InsertedBy,
#InsertedOn
)
SET #Id = SCOPE_IDENTITY()
GO
This is my test:
USE MyLibrary
GO
DECLARE #NewId int
DECLARE #date datetime
SET #date = getdate()
EXEC CategoryInsert #NewId, 'Testing', 'AL', #date
SELECT #NewId
GO
This line:
SELECT #CurrentId = Id FROM Category WHERE lower(#Name) = lower(#Name)
IF #CurrentId IS NOT NULL
The equality check will always return true because you're essentially comparing WHERE 1 = 1, which means that #CurrentID will always have a value and thus your stored procedure will always return before the INSERT happens.
I used the ANTS profiler to identify the remaining bottleneck in my C# application: the SQL Server stored procedure. I am using SQL Server 2008. Can anybody here help me increase performance, or give me pointers as to what I can do to make it better or more performant?
First, here's the procedure:
PROCEDURE [dbo].[readerSimilarity]
-- Add the parameters for the stored procedure here
#id int,
#type int
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
IF (#type=1) --by Article
SELECT id1, id2, similarity_byArticle FROM similarity WHERE (id1 = #id OR id2 = #id)
AND similarity_byArticle != 0
ELSE IF (#type=2) --by Parent
SELECT id1, id2, similarity_byParent FROM similarity WHERE (id1 = #id OR id2 = #id)
AND similarity_byParent != 0
ELSE IF (#type=3) --by Child
SELECT id1, id2, similarity_byChild FROM similarity WHERE (id1 = #id OR id2 = #id)
AND similarity_byChild != 0
ELSE IF (#type=4) --combined
SELECT id1, id2, similarity_combined FROM similarity WHERE (id1 = #id OR id2 = #id)
AND similarity_combined != 0
END
The table 'similarity' consists of two ids (id1 and id2) and a number of columns that store double values. The constraint is that id1 < id2.
Column Data
----- ----
ID1 PK, Indexed
ID2 PK, Indexed
The table contains 28.5 million entries.
Stored Procedure Background
The job of the stored procedure is to get all the rows that have the parameter id in either id1 or id2. Additionally, the column specified by the type-parameter cannot be zero.
The stored procedure is called multiple times for different ids. Although only taking ~1.6 ms per call, it sums up, when calling it 17,000 times.
The processor is running at only 25%, which seems to be because the application is waiting for the procedure call to return.
Do you see any way to speed things up?
Calling the Stored Procedure C# Code Snippet
private HashSet<NodeClustering> AddNeighbourNodes(int id)
{
HashSet<NodeClustering> resultSet = new HashSet<NodeClustering>();
HashSet<nodeConnection> simSet = _graphDataLoader.LoadEdgesOfNode(id);
foreach (nodeConnection s in simSet)
{
int connectedId = s.id1;
if (connectedId == id)
connectedId = s.id2;
// if the corresponding node doesn't exist yet, add it to the graph
if (!_setNodes.ContainsKey(connectedId))
{
NodeClustering nodeToAdd = CreateNode(connectedId);
GraphAddOuter(nodeToAdd);
ChangeWeightIntoCluster(nodeToAdd.id, s.weight);
_bFlowOuter += s.weight;
resultSet.Add(nodeToAdd);
}
}
// the nodes in the result set have been added
to the outernodes -> add to the outernodes count
_setNodes[id].countEdges2Outside += resultSet.Count;
return resultSet;
}
C# Code Background Information
This method is called each time a new id is added to the cluster. It gets all the connected nodes of that id (they are connected, when there is an entry in the db with id1=id or id2=id) via
_graphDataLoader.LoadEdgesOfNode(id);
Then it checks all the connected ids and if they are not loaded yet:
if (!_setNodes.ContainsKey(connectedId))
It Loads them:
CreateNode(connectedId);
The Method:
_graphDataLoader.LoadEdgesOfNode(id);
is called again, this time with the connectedId.
I need this to get all the connections of the new nodes with those nodes that are already in the set.
I probably could collect the ids of all nodes i need to add and call my stored procedure only once with a list of the ids.
Ideas
I could probably load the connected ids connection at once via something like
SELECT id1, id2, similarity_byArticle FROM similarity WHERE
(id1 = #id OR id2 = #id OR
id1 IN (SELECT id1 FROM similarity WHERE id2 = #id) OR
id2 IN (SELECT id1 FROM similarity WHERE id2 = #id) OR
id1 IN (SELECT id2 FROM similarity WHERE id1 = #id) OR
id2 IN (SELECT id2 FROM similarity WHERE id1 = #id))
AND similarity_byArticle != 0
but then I would get more entries than I'd need, because I would get them for already loaded nodes too (which from my tests would make up around 75% of the call).
Questions
How can I speed up the Stored Procedure?
Can I do it differently, is there a more performant way?
Can I use a List<int> as a SP-Parameter?
Any other thoughts?
If it runs that quickly, your problem is probably in the sheer number of repeated calls to the procedure. Is there a way that you could modify the stored procedure and code to return all the results the app needs in a single call?
Optimizing a query that runs in less than 2ms is probably not a fruitful effort. I doubt you will be able to shave more than fractions of a millisecond with query tweaks.
I'd try to change the application to only call this one time per ID, but if that is not possible, try this (make sure that there is an index on similarity.id1 and another index on similarity.id2):
PROCEDURE [dbo].[readerSimilarity]
-- Add the parameters for the stored procedure here
#id int,
#type int
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
IF #type=1 --by Article
BEGIN
SELECT
id1, id2,similarity_byArticle
FROM similarity
WHERE id1 = #id AND similarity_byArticle!=0
UNION
SELECT
id1, id2,similarity_byArticle
FROM similarity
WHERE id2 = #id AND similarity_byArticle!=0
END
ELSE IF #type=2 --by Parent
BEGIN
SELECT
id1, id2,similarity_byParent
FROM similarity
WHERE id1 = #id AND similarity_byParent!=0
UNION
SELECT
id1, id2,similarity_byParent
FROM similarity
WHERE id2 = #id AND similarity_byParent!=0
END
ELSE IF #type=3 --by Child
BEGIN
SELECT
id1, id2,similarity_byChild
FROM similarity
WHERE id1 = #id AND similarity_byChild!=0
UNION
SELECT
id1, id2,similarity_byChild
FROM similarity
WHERE id2 = #id AND similarity_byChild!=0
END
ELSE IF #type=4 --combined
BEGIN
SELECT
id1, id2,similarity_combined
FROM similarity
WHERE id1 = #id AND similarity_combined!=0
UNION
SELECT
id1, id2,similarity_combined
FROM similarity
WHERE id2 = #id AND similarity_combined!=0
END
END
GO
EDIT based on OP's latest comment:
The whole graph is stored in the
MSSQL-Database and I load it
successively with the procedure into
some Dictionary structures
You need to redesign your load process. You should call the database just one time to load all of this data. Since the IDs are already in a Database table, you can use a join in this query to get the proper IDs from the other table. edit your question with the table schema that contain the IDs to graph, and how they relate to the already posted code. Once you get a single query to return all the data, it will be much faster that 17,000 calls for a single row each time.
Pass all the ids into the stored proc at once, using a delimited list (Use a comma or a slash or whatever, I use a pipe character [ | ]..
Add the User defined function (UDF) listed below to your database. It will convert a delimited list into a table which you can join to your similarity table. Then in your actual stored proc, you can write...
Create Procedure GetSimilarityIDs
#IdValues Text -- #IdValues is pipe-delimited [|] list of Id Values
As
Set NoCount On
Declare #IDs Table
(rowNum Integer Primary Key Identity Not Null,
Id Integer Not Null)
Insert Into #IDs(Id)
Select Cast(sVal As Integer)
From dbo.ParseString(#IdValues, '|') -- specify delimiter
-- ---------------------------------------------------------
Select id1, id2, similarity_byArticle
From similarity s Join #IDs i On i.Id = s.Id
Where similarity_byArticle <> 0
Return 0
-- ***********************************************************
The below code is to create the generic function UDF that can parse any text string into a table of string values...:
Create FUNCTION [dbo].[ParseTextString] (#S Text, #delim VarChar(5))
Returns #tOut Table
(ValNum Integer Identity Primary Key,
sVal VarChar(8000))
As
Begin
Declare #dLLen TinyInt -- Length of delimiter
Declare #sWin VarChar(8000) -- Will Contain Window into text string
Declare #wLen Integer -- Length of Window
Declare #wLast TinyInt -- Boolean to indicate processing Last Window
Declare #wPos Integer -- Start Position of Window within Text String
Declare #sVal VarChar(8000) -- String Data to insert into output Table
Declare #BtchSiz Integer -- Maximum Size of Window
Set #BtchSiz = 7900 -- (Reset to smaller values to test routine)
Declare #dPos Integer -- Position within Window of next Delimiter
Declare #Strt Integer -- Start Position of each data value within Window
-- -------------------------------------------------------------------------
If #delim is Null Set #delim = '|'
If DataLength(#S) = 0 Or
Substring(#S, 1, #BtchSiz) = #delim Return
-- ---------------------------
Select #dLLen = Len(#delim),
#Strt = 1, #wPos = 1,
#sWin = Substring(#S, 1, #BtchSiz)
Select #wLen = Len(#sWin),
#wLast = Case When Len(#sWin) = #BtchSiz
Then 0 Else 1 End,
#dPos = CharIndex(#delim, #sWin, #Strt)
-- ------------------------------------
While #Strt <= #wLen
Begin
If #dPos = 0 -- No More delimiters in window
Begin
If #wLast = 1 Set #dPos = #wLen + 1
Else
Begin
Set #wPos = #wPos + #Strt - 1
Set #sWin = Substring(#S, #wPos, #BtchSiz)
-- ----------------------------------------
Select #wLen = Len(#sWin), #Strt = 1,
#wLast = Case When Len(#sWin) = #BtchSiz
Then 0 Else 1 End,
#dPos = CharIndex(#delim, #sWin, 1)
If #dPos = 0 Set #dPos = #wLen + 1
End
End
-- -------------------------------
Set #sVal = LTrim(Substring(#sWin, #Strt, #dPos - #Strt))
Insert #tOut (sVal) Values (#sVal)
-- -------------------------------
-- Move #Strt to char after last delimiter
Set #Strt = #dPos + #dLLen
Set #dPos = CharIndex(#delim, #sWin, #Strt)
End
Return
End
First create a view
CREATE VIEW ViewArticles
AS
SELECT id1, id2, similarity_byArticle
FROM similarity
WHERE (id1 = #id or id2 = #id)
and similarity_byArticle != 0
In your code populate all the needed ids into a table.
Create a function which takes all the ids table as parameter.
CREATE FUNCTION
SelectArticles
(
#Ids TABLE
)
RETURNS TABLE
AS
RETURN
(
SELECT id1, id2, similarity_byArticle FROM ViewArticles
INNER JOIN #Ids I ON I.Id = id1
UNION
SELECT id1, id2, similarity_byArticle FROM ViewArticles
INNER JOIN #Ids I ON I.Id = id2
)
I was wondering how I can pass either an ArrayList, List<int> or StringBuilder comma delimited list to a stored procedure such that I find a list of IDs using IN():
#myList varchar(50)
SELECT *
FROM tbl
WHERE Id IN (#myList)
In C# I am currently building the list as a string which is comma delimeted; however when using nvarchar(50) for example, as the type for the param in the stored procedure - I get an error as it can't convert '1,2,3' to int which it expects between the IN().
Any ideas? Much appreciated.
Pete
You could use a User Defined function such as
CREATE function [dbo].[csl_to_table] ( #list nvarchar(MAX) )
RETURNS #list_table TABLE ([id] INT)
AS
BEGIN
DECLARE #index INT,
#start_index INT,
#id INT
SELECT #index = 1
SELECT #start_index = 1
WHILE #index <= DATALENGTH(#list)
BEGIN
IF SUBSTRING(#list,#index,1) = ','
BEGIN
SELECT #id = CAST(SUBSTRING(#list, #start_index, #index - #start_index ) AS INT)
INSERT #list_table ([id]) VALUES (#id)
SELECT #start_index = #index + 1
END
SELECT #index = #index + 1
END
SELECT #id = CAST(SUBSTRING(#list, #start_index, #index - #start_index ) AS INT)
INSERT #list_table ([id]) VALUES (#id)
RETURN
END
Which accepts an nvarchar comma separated list of ids and returns a table of those ids as ints. You can then join on the returned table in your stored procedure like so -
DECLARE #passed_in_ids TABLE (id INT)
INSERT INTO #passed_in_ids (id)
SELECT
id
FROM
[dbo].[csl_to_table] (#your_passed_in_csl)
SELECT *
FROM
myTable
INNER JOIN
#passed_in_ids ids
ON
myTable.id = ids.id
In SQL 2008 there are table-valued-parameters, that make a friendly alternative to parsing CSV; see here for an example.
Otherwise, another option is xml - the xml data type in SQL Server allows you to read this pretty easily (although it takes more transfer bytes).