And to all experts here.. I'm a newbie to stored procedures.
I really need help, my problem is, is it possible to used a parameter as databasename
Below is my sample:
ALTER PROCEDURE [dbo].[SP_EventLogs]
(#HRTable as varchar(50))
AS
BEGIN
Set NOCOUNT ON
SELECT
a.*,LOG_ADDBY.ADDBY
FROM
EVENT_VIEWER a
INNER JOIN
(SELECT
PK, (Lastname + ', ' + Firstname + ' ' + ExtName) as ADDBY
FROM
#HRTable.[dbo].[EMP_RECORDS]) LOG_ADDBY ON a.PerformBy = LOG_ADDBY.PK
ORDER BY
a.PerformDate DESC
Set NOCOUNT OFF
END
#HRTable is my parameter.. and it returns an error.
Any suggestion would be greatly appreciated.
Thank you...
One alternative is to build a view that has the table name inside it and join to that
SELECT 't1'as tableName ,col1, col2, col3,... from t1
UNION ALL
SELECT 't2',col1, col2, col3,... from t2
where
.....
and tableName=#HRTable
But this is not really "the done thing". You can also use dynamic SQL & Exec if you must do this but maybe consider if you are doing whatever it is you are doing in the right way.
What's in the various tables that means you can't just put them into one?
As I understood #HRTable is your database name.This is not gonna to work. You should use dynamic query .
Declare Variable for query text and set it for your query then use Exec(#variable) to run the query text in variable.
Related
I have been researching this for a couple of days and feel like I am going around in circles. I have basic knowledge of SQL but there are many areas I do not understand.
I have a table that stores the names and fields of all the other tables in my database.
tblFields
===================================================
TableName FieldName BookmarkName
---------------------------------------------------
Customer FirstName CustomerFirstName
Customer LastName CustomerLastName
Customer DOB CustomerDOB
I want to write a SELECT statement like the following but i am unable to get it work:
SELECT (SELECT [FieldName] FROM [TableName]) FROM tblFields
Is this possible? The application I have developed requires this for user customization of reports.
If i understand what you are trying to do, i think this will help you. It is not pretty and it works for SQL Server 2005 and above, but maybe this is what you are looking for:
declare #tableName nvarchar(100)
declare #sqlQuery nvarchar(max)
declare #fields varchar(500)
set #tableName = 'YourTableName'
set #fields = ''
select #fields = #fields + QUOTENAME(t.fieldname) + ',' from (
select distinct fieldname from tblfields where tablename = #tableName)t
set #sqlQuery = 'select ' + left(#fields, LEN(#fields)-1) + ' from ' + QUOTENAME(#tableName)
execute sp_executesql #sqlQuery
Edit: As Martin suggested, i edited so that the columns and tablename are using QUOTENAME
If I understand correctly what you are trying to do, you are probably better off doing this as two separate queries from your program. One which gets the fields you want to select which you then use in your program to build up the second query which actually gets the data.
If it must be done entirely in SQL, then you will need to tell us what database you are using. If it is SQL Server, you might be able to user a cursor over the first query to build up the second query which you then execute with the sp_executesql stored procedure. But doing doing it outside of SQL would be recommended.
I am trying to learn some SQL Injection and wanted to ask if any other query other than select could be executed within a SQL IN Operator.
Normal IN operator syntax is this
select * from tableX where ID IN (select userId from tableY where colX='Y')
I would want to pass in a sql stmt via querystring to delete all users in a table.
Is this possible to do if this injected sql is executed within a IN operator.
This is what I want to achieve
select * from tableX where ID IN (delete tableY)
or
select * from tableX where ID IN (update tableX set ID=100 where 1=1)
or
select * from tableX where ID IN (exec(N'delete tableY'))
I keep getting weird syntax errors. May be this is not as per the sql spec. But in case anyone knows of valid subqueries which might help me achieve the goal, please post.
Edit: Should have also added this.
The system already has couple of defenses.
This value is passed to a SP as a parametrized value.
But one the whole there is one weakness
The only weak point I see which I am trying to exploit is the construction of the sql string in the SP using the passed in value.
set #where = #where + 'ID IN (' + #htmlEncodedParametrizedParam + ')
So all I have to break this code is send in only a pure sql string (no ; or ) which could be used within an IN operator.
Edit2: Coded up to verify #Dan's answer. No avail.
Create procedure HackTest
#qsVal nvarchar(200) = ''
As
Begin
declare #sql as nvarchar(1000)
set #sql = 'select * from AdventureWorks2008.Person.Person' + 'where PersontType IN (' + #qsVal + ')'
exec sp_executesql #sql
end
Below are the request and what MS Sql server profiler captured for these requests
--http://localhost:11727/SampleSite/Default.aspx?a=NULL);delete%20tableY;--
exec hackTest #qsVal='NULL);delete tableY;--'
--http://localhost:11727/SampleSite/Default.aspx?a=NULL);update%20tableX%20set%20ID=100;--
exec hackTest #qsVal='NULL);update tableX set ID=100;--'
No, the IN operator does only allow a subquery or a list of expressions:
test_expression [ NOT ] IN
( subquery | expression [ ,...n ]
)
So you can’t use a DELETE or an UPDATE statement there but only a SELECT statement.
However, since SQL Server support batches, you could simply append your own statement after the SELECT statement:
SELECT … ; DELETE …
you could simulate multiple queries, for example, this is your query, parameter will be replaced later by your program:
SELECT * FROM tableX WHERE ID IN (<somestring>)
if you were to replace with this text:
1); DROP TABLE tableY;--
you'll get executable query like this:
SELECT * FROM tableX WHERE ID IN (1); DROP TABLE tableY;--)
If the application constructs the SQL statement with string concatenation like:
selectQuery = "select * from tableX where ID IN (" + queryString + ")";
This particular SQL injection vulnerability can be exploited by adding "NULL);" followed by the SQL statement to be injected, and then a comment marker. Example query string values:
NULL);delete tableY;--
NULL);update tableX set ID=100;--
Of course, one should never pass a SQL statement via a query string. The best defense against SQL injection is simply to use parameterized queries rather than building statements with string concatenation from untrusted sources.
I have multi databases with same structure its name like that "Client1234" the different in numbers beside "client" i have table called "Transactions" inside each database and i want to run query to get count all raws in "transactions" table in all databases.
also when i select database i need to check it has the client word and it has numbers beside the word.
Try to use sp_msforeachdb stored procedure like so:
create table #temp ([rows] int, [client] varchar(100))
exec sp_msforeachdb '
if ''?'' like ''Client%'' and exists(select * from ?.sys.tables t where t.name = ''Transactions'')
begin
insert into #temp select count(*), ''?'' from ?..Transactions
end
'
select * from #temp
drop table #temp
You can use dynamic SQL to create these queries:
select 'select count(*) from ' + name + '.dbo.transactions'
from master..sysdatabases
where name like 'Client%'
and isnumeric(substring(name,6,1))
This would return a result set with each row being a SQL query to count a specific database. It could be consumed by a programming language, used as a cursor, etc.. If you provide more detail I may be able to provide a better example.
When using Fosco's method, it is a good idea to put in brackets [] around the database name:
SELECT 'SELECT count(*) FROM ' + '[' + name + ']' + '.dbo.transactions'
FROM master..sysdatabases
WHERE name like 'Client%' and isnumeric(substring(name,6,1))
If the name and number of the databases you wish to query is not known beforehand then you can only do this by using a dynamic query. You'll need to generate a script like
SELECT COUNT(*) FROM Client1.dbo.Transactions
SELECT COUNT(*) FROM Client2.dbo.Transactions
...
Of course you need to have your appropriate permissions in place for each database.
Say if using sp_helplognis, want to view result set with filter UserName=db_owner. Is there any way besides output the original result set to a temp table and then query on that temp table? Thanks.
Don't use sp_helplogins: use sys.server_principals
SELECT * FROM sys.server_principals WHERE name = 'bob'
If you want the 2nd resultset of sp_helplogins, then you'd have to use c# or something because loading a temp table will only trap the 1st resultset.
The equivalent of sys.server_principals is sys.database_principals too
FYI: db_owner is in every database already. What are you really trying to do...?
Edit, after comment, something like:
EXEC sp_MSForEachDb '
USE ?
IF DATABASE_PRINCIPAL_ID (''myloginname'') IS NOT NULL
EXEC sp_addrolemember ''db_datareader'', ''myloginname''
'
You can divert the resultset of a sproc into a new table, e.g.
create table #results (...)
insert into #results execute myproc
select * from #results where ...
It's a bit fiddly because you have to reverse-engineer the resultset data types pretty exactly to avoid casting errors.
It looks like #temptables created using dynamic SQL via the EXECUTE string method have a different scope and can't be referenced by "fixed" SQLs in the same stored procedure.
However, I can reference a temp table created by a dynamic SQL statement in a subsequence dynamic SQL but it seems that a stored procedure does not return a query result to a calling client unless the SQL is fixed.
A simple 2 table scenario:
I have 2 tables. Let's call them Orders and Items. Order has a Primary key of OrderId and Items has a Primary Key of ItemId. Items.OrderId is the foreign key to identify the parent Order. An Order can have 1 to n Items.
I want to be able to provide a very flexible "query builder" type interface to the user to allow the user to select what Items he want to see. The filter criteria can be based on fields from the Items table and/or from the parent Order table. If an Item meets the filter condition including and condition on the parent Order if one exists, the Item should be return in the query as well as the parent Order.
Usually, I suppose, most people would construct a join between the Item table and the parent Order tables. I would like to perform 2 separate queries instead. One to return all of the qualifying Items and the other to return all of the distinct parent Orders. The reason is two fold and you may or may not agree.
The first reason is that I need to query all of the columns in the parent Order table and if I did a single query to join the Orders table to the Items table, I would be repoeating the Order information multiple times. Since there are typically a large number of items per Order, I'd like to avoid this because it would result in much more data being transfered to a fat client. Instead, as mentioned, I would like to return the two tables individually in a dataset and use the two tables within to populate a custom Order and child Items client objects. (I don't know enough about LINQ or Entity Framework yet. I build my objects by hand). The second reason I would like to return two tables instead of one is because I already have another procedure that returns all of the Items for a given OrderId along with the parent Order and I would like to use the same 2-table approach so that I could reuse the client code to populate my custom Order and Client objects from the 2 datatables returned.
What I was hoping to do was this:
Construct a dynamic SQL string on the Client which joins the orders table to the Items table and filters appropriate on each table as specified by the custom filter created on the Winform fat-client app. The SQL build on the client would have looked something like this:
TempSQL = "
INSERT INTO #ItemsToQuery
OrderId, ItemsId
FROM
Orders, Items
WHERE
Orders.OrderID = Items.OrderId AND
/* Some unpredictable Order filters go here */
AND
/* Some unpredictable Items filters go here */
"
Then, I would call a stored procedure,
CREATE PROCEDURE GetItemsAndOrders(#tempSql as text)
Execute (#tempSQL) --to create the #ItemsToQuery table
SELECT * FROM Items WHERE Items.ItemId IN (SELECT ItemId FROM #ItemsToQuery)
SELECT * FROM Orders WHERE Orders.OrderId IN (SELECT DISTINCT OrderId FROM #ItemsToQuery)
The problem with this approach is that #ItemsToQuery table, since it was created by dynamic SQL, is inaccessible from the following 2 static SQLs and if I change the static SQLs to dynamic, no results are passed back to the fat client.
3 around come to mind but I'm look for a better one:
1) The first SQL could be performed by executing the dynamically constructed SQL from the client. The results could then be passed as a table to a modified version of the above stored procedure. I am familiar with passing table data as XML. If I did this, the stored proc could then insert the data into a temporary table using a static SQL that, because it was created by dynamic SQL, could then be queried without issue. (I could also investigate into passing the new Table type param instead of XML.) However, I would like to avoid passing up potentially large lists to a stored procedure.
2) I could perform all the queries from the client.
The first would be something like this:
SELECT Items.* FROM Orders, Items WHERE Order.OrderId = Items.OrderId AND (dynamic filter)
SELECT Orders.* FROM Orders, Items WHERE Order.OrderId = Items.OrderId AND (dynamic filter)
This still provides me with the ability to reuse my client sided object-population code because the Orders and Items continue to be returned in two different tables.
I have a feeling to, that I might have some options using a Table data type within my stored proc, but that is also new to me and I would appreciate a little bit of spoon feeding on that one.
If you even scanned this far in what I wrote, I am surprised, but if so, I woul dappreciate any of your thoughts on how to accomplish this best.
You first need to create your table first then it will be available in the dynamic SQL.
This works:
CREATE TABLE #temp3 (id INT)
EXEC ('insert #temp3 values(1)')
SELECT *
FROM #temp3
This will not work:
EXEC (
'create table #temp2 (id int)
insert #temp2 values(1)'
)
SELECT *
FROM #temp2
In other words:
Create temp table
Execute proc
Select from temp table
Here is complete example:
CREATE PROC prTest2 #var VARCHAR(100)
AS
EXEC (#var)
GO
CREATE TABLE #temp (id INT)
EXEC prTest2 'insert #temp values(1)'
SELECT *
FROM #temp
1st Method - Enclose multiple statements in the same Dynamic SQL Call:
DECLARE #DynamicQuery NVARCHAR(MAX)
SET #DynamicQuery = 'Select * into #temp from (select * from tablename) alias
select * from #temp
drop table #temp'
EXEC sp_executesql #DynamicQuery
2nd Method - Use Global Temp Table:
(Careful, you need to take extra care of global variable.)
IF OBJECT_ID('tempdb..##temp2') IS NULL
BEGIN
EXEC (
'create table ##temp2 (id int)
insert ##temp2 values(1)'
)
SELECT *
FROM ##temp2
END
Don't forget to delete ##temp2 object manually once your done with it:
IF (OBJECT_ID('tempdb..##temp2') IS NOT NULL)
BEGIN
DROP Table ##temp2
END
Note: Don't use this method 2 if you don't know the full structure on database.
I had the same issue that #Muflix mentioned. When you don't know the columns being returned, or they are being generated dynamically, what I've done is create a global table with a unique id, then delete it when I'm done with it, this looks something like what's shown below:
DECLARE #DynamicSQL NVARCHAR(MAX)
DECLARE #DynamicTable VARCHAR(255) = 'DynamicTempTable_' + CONVERT(VARCHAR(36), NEWID())
DECLARE #DynamicColumns NVARCHAR(MAX)
--Get "#DynamicColumns", example: SET #DynamicColumns = '[Column1], [Column2]'
SET #DynamicSQL = 'SELECT ' + #DynamicColumns + ' INTO [##' + #DynamicTable + ']' +
' FROM [dbo].[TableXYZ]'
EXEC sp_executesql #DynamicSQL
SET #DynamicSQL = 'IF OBJECT_ID(''tempdb..##' + #DynamicTable + ''' , ''U'') IS NOT NULL ' +
' BEGIN DROP TABLE [##' + #DynamicTable + '] END'
EXEC sp_executesql #DynamicSQL
Certainly not the best solution, but this seems to work for me.
I would strongly suggest you have a read through http://www.sommarskog.se/arrays-in-sql-2005.html
Personally I like the approach of passing a comma delimited text list, then parsing it with text to table function and joining to it. The temp table approach can work if you create it first in the connection. But it feel a bit messier.
Result sets from dynamic SQL are returned to the client. I have done this quite a lot.
You're right about issues with sharing data through temp tables and variables and things like that between the SQL and the dynamic SQL it generates.
I think in trying to get your temp table working, you have probably got some things confused, because you can definitely get data from a SP which executes dynamic SQL:
USE SandBox
GO
CREATE PROCEDURE usp_DynTest(#table_type AS VARCHAR(255))
AS
BEGIN
DECLARE #sql AS VARCHAR(MAX) = 'SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = ''' + #table_type + ''''
EXEC (#sql)
END
GO
EXEC usp_DynTest 'BASE TABLE'
GO
EXEC usp_DynTest 'VIEW'
GO
DROP PROCEDURE usp_DynTest
GO
Also:
USE SandBox
GO
CREATE PROCEDURE usp_DynTest(#table_type AS VARCHAR(255))
AS
BEGIN
DECLARE #sql AS VARCHAR(MAX) = 'SELECT * INTO #temp FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = ''' + #table_type + '''; SELECT * FROM #temp;'
EXEC (#sql)
END
GO
EXEC usp_DynTest 'BASE TABLE'
GO
EXEC usp_DynTest 'VIEW'
GO
DROP PROCEDURE usp_DynTest
GO