Creating a dynamic Stored Procedure on MSSQL SERVER 2008 R2 - c#

I have a System that fetches its data from different DBs on the same server.This DBs are newly attached to the server annually.e.g. at the at the beginning of 2013, a db called 2012 is attached.
So I want to create a stored procedure(SP) that fetches the user's input which can be anything from 2005(year). so based on the year the user enters, the SP should go to that db(whose name will be the year the user entered) and search for the data (with its parameter being the year the user entered) inside the db which will also has a table with the same name as the db(i.e the table will have the same name as the year name).
Hope this makes sense

It would be a good idea to paramatarize the query.
e.g.
CREATE PROC usp_bar
(
#ID INT
)
AS
BEGIN
DECLARE #SQL NVARCHAR(100)
DECLARE #Params NVARCHAR(100)
SET #SQL = N'SELECT * FROM [Table] WHERE ID = #ID'
SET #Params = N'#ID INT'
EXEC sp_executesql #SQL, #Params, #ID = 5
END
Check out this

I am no DBA so take this with a grain of salt and understand there may be a better way to do this but what you will probably have to do is something like this:
CREATE PROCEDURE usp_foo
#Year varchar(4)
AS
DECLARE #sql varchar(255)
#sql = 'SELECT * from [' + #Year + '].[owner].[table]'
sp_executesql #sql
Of course the user calling this sproc will have to A) have permissions to call system sprocs B) have permission to access the yearly database
Additionally, instead of going the SQL route, you could just make a dynamic connection string that you could populate with the correct catalog then issue your SQL queries directly to the database. Personally I would prefer that over using dynamic SQL.

One thing you could do, is take a look at synonyms, you could create one for each year:
http://sommarskog.se/dynamic_sql.html#Dyn_DB
CREATE SYNONYM otherdbtbl FOR otherdb.dbo.tbl
I'd recommend his site, it's full of good stuff, it's a great read :)
As to whether this works well for you, I'd expect it depends on how many tables you have in each DB. If it's a few this may work, if it's hundreds maybe another approach may work better - like abszero's suggestion of doing the switching in the application tier?

Related

Telerik Report avoid datasource execution if not required

My application is multilingual application. In my application, I have created a report which has 2 data source. Both call same procedures and fetch same data. The difference only is based on culture, the data will be filled and table will be displayed.
It is working fine. But the problem I am facing is, though I have applied condition to display table based on culture, it is executing both data source. That is I have one unnecessary database call.
Is there any alternative to solve this?
If your data source comes from SQL, I believe you can do this in a stored procedure with a report parameter. (I assume your tables are in the same database, but you can adjust accordingly)
I will use the AdventureWorks database, and my sp selects the "Name" column from a given table:
CREATE PROCEDURE [dbo].[test] #table nvarchar(100) as
BEGIN
declare #sql nvarchar(1000)
SET #sql = 'SELECT Name FROM ' + #table
exec SP_EXECUTESQL #sql
END;
Then, configure the datasource to execute the stored procedure, taking in a report parameter for #table. In my example, reportParameter="Person.AddressType" or "Person.ContactType" will give different results.
In your case, change the reportParameter according to culture. Now you will only need one table instead of two as well.

In Function Import, The selected stored procedure returns no columns

I have written a stored procedure to insert values into a table where the primary key will be auto incremented. when I try to import this in Visual Studio 2013, In function Imports when I select "Get Column Information" it says "The selected procedure or function" returns no columns.
I read about it many articles and also included SET FMTONLY OFF in my code but it still does not work. Amateur in ASP.Net and C#. So can anyone explain to me What to do in a clear manner
USE [DB_Name]
GO
/****** Object: StoredProcedure [dbo].[usp_makePost] Script Date: 04-04-2015 19:16:04 ******/
SET FMTONLY OFF
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE procedure [dbo].[usp_makePost]
#FK_strUser_Id varchar(11),
#strPost_Title varchar(100),
#strPost_Content varchar(1000),
#dTime_of_post datetime,
#iUp_Vote int,
#iDown_Vote int,
#FK_strRootword_Id varchar(11)
as
begin
DECLARE #PK_strPost_Id VARCHAR(11);
DECLARE #PreFix VARCHAR(10) = 'POST';
DECLARE #Id INT;
SELECT #Id = ISNULL(COUNT(PK_strPost_Id),0) + 1 FROM Tbl_Post
SELECT #PK_strPost_Id = #PreFix + RIGHT('0000' + CAST(#Id AS VARCHAR(7)), 7)
insert into Tbl_Name values(#PK_strPost_Id,#FK_strUser_Id,#strPost_Title,#strPost_Content,#dTime_of_post,#iUp_Vote,#iDown_Vote,#FK_strRootword_Id)
end
Your stored procedure doesn't do any data retrieve operation (ie, any SELECT). It just does an INSERT plus some variable manipulation. Those SELECTs out there only assign variables too, so nothing really produces any kind of result set.
Therefore client programs are completely right in that there are no columns or any kind of output from this procedure. Maybe you intended to add some sort of return table?
Think of the stored procedure as a data source for your front end application. Now for it to have data, it has to end with a SELECT clause, because only then can it have data. Clearly your application is expecting data. So without going into much details,
either you need to tell your application to stop expecting data.
or, modify the procedure so that it starts giving data.
Probably you would need to add a SELECT * FROM Tbl_Name in the end of stored proc or something similar.

using sqlcommand with multiple ids

So this works great:
select name from users where id = #id
However, that only selects 1 guy. Say I have 3 IDs instead, normally the SQL would look something like this (without using parameters)
select name from users where id in (4,6,9)
However, it doesn't seem to work when I write
select name from users where id in (#IDs)
and insert a list into #IDs, like this
cmd.Parameters.AddWithValue("#IDs", userIDs);
Is there any way to do what I'm trying to? It's important to note that the sql I'm calling is (and has to be) a stored procedure.
There are two ways to do this. The first is by passing a string to a stored procedure and then adding it to a dynamic query:
-- #IDs = '4,6,9'
DECLARE #MyQuery nvarchar(max)
SET #MyQuery = N'SELECT name FROM users WHERE id IN (' + #IDs + ')'
EXEC(#MyQuery)
On the other hand, if you are using SQL Server 2008 or later, you can use a table-valued parameter (this is my preference).
First, create a user-defined table type:
CREATE TYPE IDList AS TABLE (
id int
)
THEN, use the user defined type as the type for your parameter:
DECLARE #IDs IDList
INSERT INTO #IDs (ID) VALUES (4),(6),(9)
SELECT name FROM users u INNER JOIN #IDs i WHERE u.id = i.id
If you are using .NET to consume a stored procedure, you can find sample code for user-defined SQL types on MSDN.
You can create a dynamic SQL query inside your stored procedure:
DECLARE #SQLQuery AS NVARCHAR(500)
SET #SQLQuery = 'select name from users where id in ( ' + #userIDs + ')'
EXECUTE(#SQLQuery)
You'll have to be careful and sanitize the contents of #userIDs though to prevent SQL injection attacks.
On our side we are using iBatis.Net to manage this. Execute query sounds quite ugly but it still does the trick.
We were mostly using string split in SQL. See [question]How do I split a string so I can access item x?

Strange C# to SQL Dynamic Query Execution

All, I have a dynamic SQL Query that I am executing from a C# application. The problem query is an INSERT statement, which is run from within a C# loop, being executed sequentially on many databases to create a single data warehouse [database]. I have run this code one 100+ databases in a single batch without problem; however, I have just come across one specific database where the query
DECLARE #DbName NVARCHAR(128);
SET #DbName = (SELECT TOP 1 [DbName]
FROM [IPACostAdmin]..[TmpSpecialOptions]);
DECLARE #FilterSql NVARCHAR(MAX);
SET #FilterSql = (SELECT TOP 1 [AdditionalSQL]
FROM [IPACostAdmin]..[TmpSpecialOptions]);
DECLARE #SQL NVARCHAR(MAX);
DECLARE #SQL1 NVARCHAR(MAX);
DECLARE #SQL2 NVARCHAR(MAX);
SET #SQL1 =
'INSERT INTO [' + #DbName + ']..[Episode] WITH(TABLOCK)
([EstabID],..., [InclFlag]) ';
SET #SQL2 =
'SELECT
[EstabID],..., [InclFlag]
FROM [B1A] ' + #FilterSql + ';';
SET #SQL = #SQL1 + #SQL2;
EXEC sp_executesql #SQL;
Goes from taking roughly three seconds for an insert of 20,000-30,000 records to 40+ minutes! Now, after long deliberation and experiments, I have just worked out the fix for this; it is to use
EXEC sp_executesql #SQL WITH RECOMPILE;
This brings it back down to < 2s for the insert.
This SQL is executed from the application once for each database in the batch, the current execution of this statement should be totally separate from the preceding ones as far as the server is concerned (as I understand it), but it is not; it seems SQL is cashing the dynamic SQL in this case.
I would like to know what is happening here for this single site? Where will I need to ensure I use the RECOMPILE option in future to prevent such issues?
Thanks for your time.
_Note. I appreciate that this recompiles the query, but I am baffelled as to why the server is using the same execution plan in the first place. each time this query is run it is against a different database using a different Initial Catalog using a different SqlConnection.
when you do RECOMPILE, sql server will generate each time new execution plan and execute it. other wise it will try to use an existing execution plan stored in the procedure cache, which may be wrong for the current query as in dynamic query, conditions and parameters get changed each time it executes..

How do I get Linq to SQL to recognize the result set of a dynamic Stored Procedure?

I'm using Linq-to-SQL with a SQL Server backend (of course) as an ORM for a project. I need to get the result set from a stored procedure that returns from a dynamically-created table. Here's what the proc looks like:
CREATE procedure [RetailAdmin].[TitleSearch] (
#isbn varchar(50), #author varchar(50),
#title varchar(50))
as
declare #L_isbn varchar(50)
declare #l_author varchar(50)
declare #l_title varchar(50)
declare #sql nvarchar(4000)
set #L_isbn = rtrim(ltrim(#isbn))
set #l_author = rtrim(ltrim(#author))
set #l_title = rtrim(ltrim(#title))
CREATE TABLE #mytemp(
[storeid] int not NULL,
[Author] [varchar](100) NULL,
[Title] [varchar](400) NULL,
[ISBN] [varchar](50) NULL,
[Imprint] [varchar](255) NULL,
[Edition] [varchar](255) NULL,
[Copyright] [varchar](100) NULL,
[stockonhand] [int] NULL
)
set #sql = 'select a.storeid, Author,Title, thirteendigitisbn ISBN,
Imprint,Edition,Copyright ,b.stockonhand from ods.items a join ods.inventory b on
a.itemkey = b.itemkey where b.stockonhand <> 0 '
if len(#l_author) > 0
set #sql = #sql + ' and author like ''%'+#L_author+'%'''
if len(#l_title) > 0
set #sql = #sql + ' and title like ''%'+#l_title+'%'''
if len(#L_isbn) > 0
set #sql = #sql + ' and thirteendigitisbn like ''%'+#L_isbn+'%'''
print #sql
if len(#l_author) <> 0 or len(#l_title) <> 0 or len(#L_isbn) <> 0
begin
insert into #mytemp
EXECUTE sp_executesql #sql
end
select * from #mytemp
drop table #mytemp
I didn't write this procedure, but may be able to influence a change if there's a really serious problem.
My present problem is that when I add this procedure to my model, the designer generates this function:
[Function(Name="RetailAdmin.TitleSearch")]
public int TitleSearch([Parameter(DbType="VarChar(50)")] string isbn,
[Parameter(DbType="VarChar(50)")] string author,
[Parameter(DbType="VarChar(50)")] string title)
{
IExecuteResult result = this.ExecuteMethodCall(this,
((MethodInfo)(MethodInfo.GetCurrentMethod())), isbn, author, title);
return ((int)(result.ReturnValue));
}
which doesn't look anything like the result set I get when I run the proc manually:
Can anybody tell me what's going wrong here?
This is basically the same problem as this question but due to the poor phrasing from the OP it was never really answered.
Thanks Marc for your reply. I will see about making the changes you suggested.
The problem was the temp table. Linq to Sql just doesn't know what to do with them. This was particularly difficult to diagnose, because Visual Studio caches information about stored procs, so when it initially failed to find a result set it set the return as a default integer type and didn't update when I made changes to the stored proc. Getting VS to recognize a change requires you to:
Delete proc from the dbml
delete the server connection from Server Explorer
save the dbml to force a recompile
close the project and restart VS
recreate the server connection and import the proc
You might not have to do every one of those steps, but that's what worked for me. What you need to do, if you must use a temp table, is to create a barebones proc that simply returns the correct schema, and then alter it to do what you want after you've imported it into the OR Designer.
First - IMPORTANT - your SQL is vulnerable to injection; the inner command should be parameterized:
if len(#l_author) > 0
set #sql = #sql + ' and author like ''%''+#author+''%'''
EXECUTE sp_executesql #sql, N'#author varchar(100)', #L_author
This passes the value of #L_author in as the #author parameter in the dynamic command - preventing injection attacks.
Second - you don't really need the temp table. It isn't doing anything for you... you just INSERT and SELECT. Perhaps just EXEC and let the results flow to the caller naturally?
In other circumstances a table-variable would be more appropriate, but this doesn't work with INSERT/EXEC.
Are the columns the same for every call? If so, either write the dbml manually, or use a temp SP (just with "WHERE 1=0" or something) so that the SET FMT_ONLY ON can work.
If not (different columns per usage), then there isn't an easy answer. Perhaps use regular ADO.NET in this case (ExecuteReader/IDataReader - and perhaps even DataTable.Fill).
Of course, you could let LINQ take the strain... (C#):
...
if(!string.IsNullOrEmpty(author)) {
query = query.Where(row => row.Author.Contains(author));
}
...
etc
There's no real easy way to do this. I've had the same problem in the past. I think the issue is that Linq to Sql has no way of "figuring out" which type will be returned since you're building up the SELECT statement at execution time. What I did to get around this, was in the stored proc, I did just a select and selected all the columns that I possibly needed. Then, I had Linq to Sql generate the function based on that. Then, I went back to SQL and changed the stored proc back to the way it's supposed to be. The trick here is not to regenerate your DBML.

Categories

Resources