C# SQL Top as parameter - sql

Trying to parameterize the value of TOP in my sql statement.
SELECT TOP #topparam * from table1
command.Parameters.Add("#topparam",SqlDbType.VarChar, 10).Value = somevalue.ToString();
This doesn't seem to work. Anyone have any suggestions?
Just to clarify, I don't want to use stored procedures.

In SQL Server 2005 and above, you can do this:
SELECT TOP (#topparam) * from table1

You need to have at least SQL Server 2005. This code works fine in 2005/8 for example ...
DECLARE #iNum INT
SET #iNum = 10
SELECT TOP (#iNum) TableColumnID
FROM TableName
If you have SQL Server 2000, give this a try ...
CREATE PROCEDURE TopNRecords
#intTop INTEGER
AS
SET ROWCOUNT #intTop
SELECT * FROM SomeTable
SET ROWCOUNT 0
GO

You could write an inline query:
EXEC 'SELECT TOP ' + #topparam + ' * FROM ... '
Parse it as an int and that will prevent a SQL injection attack.

Related

postgres sql / dbweaver - Using a variable for tablename?

One of the SQL Queries use the same table name again and again -
Example -
select
rr.jdoc as child_node, json_agg(parent_rr.jdoc)::jsonb as parent_node , array_length(array_agg(parent_rr.jdoc)::jsonb[], 1) as count
from MYTABLE rr, MYTABLE parent_rr
where
parent_rr.jdoc #> (rr.jdoc->'somefield')::jsonb
group by rr.jdoc
UNION
select rr.jdoc, NULL as parent_id, null as pcount
from MYTABLE rr where
not (rr.jdoc ?? 'somefiled')
and ((rr.jdoc->'crazyfiled'->>'doublecrazyfiled')<>'gotyou')
You can see the same MYTABLE is used 3 times. The thing is, I have to run the same query for different tables i.e the MYTABLE substituted.
So I am trying to find - if anything like below is possible -
SET TABLENAME=CUS_DELTA --//then use the above like -
select * from $TABLENAME;
I am using postgres 13.5 with DBeaver for running the queries.
If not SQL, does DBeaver provide any such means.
If SQL can not support it, I am ok to use Dynamic SQL / PL SQL (though i have not idea about these) - if its not going to be too complicate for the above case. But I am not aware about the code, so request to share the same through an answer.
Read this article about Dynamic SQL carefully. Example from the docs:
EXEC SQL BEGIN DECLARE SECTION;
const char *stmt = "INSERT INTO test1 VALUES(?, ?);";
EXEC SQL END DECLARE SECTION;
EXEC SQL PREPARE mystmt FROM :stmt;
...
EXEC SQL EXECUTE mystmt USING 42, 'foobar';
Basically this way you can simply pass values. Now, your values are dynamic as well, but no worries. Declare your tablename as varchar and SELECT tbname INTO yourvariable FROM sometable. Then you can pass this variable to your dynamic query.
EDIT:
Minimalistic example:
Setting a variable to hard-coded value
SET TABLENAME = 'CUS_DELTA';
Setting a variable to a dynamic table name
SELECT tbname
INTO TABLENAME
FROM yourtable
And then let's use our TABLENAME variable like this:
EXEC SQL BEGIN DECLARE SECTION;
const char *stmt = "SELECT * FROM ?";
EXEC SQL END DECLARE SECTION;
EXEC SQL PREPARE mystmt FROM :stmt;
EXEC SQL EXECUTE mystmt USING TABLENAME;
The above is untested.

Store a database name in variable & then using it dynamically

I have a Table in my Database Which have name of all the Database of my Server
Table Look like
create Table #db_name_list(Did INT IDENTITY(1,1), DNAME NVARCHAR(100))
INSERT INTO #db_name_list
SELECT 'db_One ' UNION ALL
SELECT 'db_Two' UNION ALL
SELECT 'db_Three' UNION ALL
SELECT 'db_four' UNION ALL
SELECT 'db_five'
select * from #db_name_list
I have so many SP in my Database..Which uses multiple table and Join Them..
At Present I am using the SQL code like
Select Column from db_One..Table1
Left outer join db_two..Table2
on ....some Condition ....
REQUIREMENT
But I do not want to HARDCODE the DATABASE Name ..
I want store DataBase name in Variable and use that .
Reason :: I want to restore same Database with Different name and want to Run those SP..At Present we Cant Do ,Because I have used db_One..Table1
or db_two..Table2
I want some thing like ...
/SAMPLE SP/
CREATE PROCEDURE LOAD_DATA
AS
BEGIN
DECLARE #dbname nvarchar(500)
set #dbname=( SELECT DNAME FROM #db_name_list WHERE Did=1)
set #dbname2=( SELECT DNAME FROM #db_name_list WHERE Did=2)
PRINT #DBNAME
SELECT * FROM #dbname..table1
/* or */
SELECT * FROM #dbname2.dbo.table1
END
i.e using Variable Instead of Database name ..
But it thow error
"Incorrect syntax near '.'."
P.S This was posted by some else on msdn but the answer there was not clear & I had the same kind of doubt. So please help
You can't use a variable like this in a static sql query. You have to use the variable in dynamic sql instead, in order to build the query you want to execute, like:
DECLARE #sql nvarchar(500) = 'SELECT * FROM ' + #dbname + '.dbo.mytable'
EXEC(#sql);
There seem to be a couple of options for you depending on your circumstances.
1. Simple - Generalise your procedures
Simply take out the database references in your stored procedure, as there is no need to have an explicit reference to the database if it is running against the database it is stored in. Your select queries will look like:
SELECT * from schema.table WHERE x = y
Rather than
SELECT * from database.schema.table WHERE x = y
Then just create the stored procedure in the new database and away you go. Simply connect to the new database and run the SP. This method would also allow you to promote the procedure to being a system stored procedure, which would mean they were automatically available in every database without having to run CREATE beforehand. For more details, see this article.
2. Moderate - Dynamic SQL
Change your stored procedure to take a database name as a parameter, such as this example:
CREATE PROCEDURE example (#DatabaseName VARCHAR(200))
AS
BEGIN
DECLARE #SQL VARCHAR(MAX) = 'SELECT * FROM ['+#DatabaseName+'].schema.table WHERE x = y'
EXEC (#SQL)
END

Finding number of columns returned by a query

How can I get the number of columns returned by an SQL query using SQL Server?
For example, if I have a query like following:
SELECT *
FROM A1, A2
It should return the total number of columns in table A1 + total number of columns in table A2. But the query might be more complicated.
Here is one method:
select top 0
into _MYLOCALTEMPTABLE
from (your query here) t
select count(*)
from Information_Schema.Columns c
where table_name = '_MYLOCALTEMPTABLE'
You can do something similar by creating a view.
You didn't specify your SQL Server version but I'm assuming it's not 2012. However, future readers of this question might be on 2012+ so I'm posting this answer for them.
SQL Server 2012 provides a set of procedures to provide more meta-data about queries and parameters. In this case, the stored procedure sp_describe_first_result_set will provide a handy tabular form.
There is also a DMO function, sys.dm_exec_describe_first_result_set, to provide similar content which is what you'd want to use in your example
DECLARE
-- Your query goes here
#query nvarchar(4000) = N'SELECT * FROM mdm.tblStgBatch AS TSB';
-- Tabular results
EXECUTE sys.sp_describe_first_result_set #tsql = #query;
-- Simple column count
SELECT
COUNT(1) AS column_count
FROM
sys.dm_exec_describe_first_result_set(#query, NULL, 0);
The new metadata discovery options are replacing FMTONLY which is how one would solve this problem prior to 2012. My TSQL chops are apparently not strong enough to do anything useful with it and instead I'd have to bail out to a .NET language to work with the output of FMTONLY.
SET FMTONLY ON;
SELECT *
FROM A1, A2;
SET FMTONLY OFF;
Try this;
--Insert into a temp table (this could be any query)
SELECT *
INTO #temp
FROM [yourTable]
--Select from temp table
SELECT * FROM #temp
--List of columns
SELECT COUNT(name) NumOfColumns FROM tempdb.sys.columns WHERE object_id =
object_id('tempdb..#temp');
--drop temp table
DROP TABLE #temp
Ugly I know:
SELECT COUNT(*) +
(
SELECT COUNT(*)
FROM information_schema.columns
WHERE table_name = 'A1'
)
FROM information_schema.columns
WHERE table_name = 'A2'

Sql short circuit OR or conditional exists in where clause

I'm trying to force sql server to make a short circuit OR comparison on some fields. On the left side of the Or I have a simple variable comparison and on the right side I have a pretty 'heavy' subquery.
WHERE
(#Var = 'DefaultValue' ) OR
Exists(select * from atable)
Is there any way to only execute the right side of the or statement if the first statement is false.
I've tried case and if else statements but can't find any working syntax.
I'm using a MS SQL server
You can't do what you want in a single SQL statement. You can do something like this however in a stored proc
If #Var = 'DefaultValue' then
BEGIN
SELECT * FROM table
END
ELSE
BEGIN
SELECT * FROM table
WHERE Exists(select * from atable)
END
If you've got a lot of inputs you should consider Dynamic SQL
How about this?
WHERE 1 = CASE #Var
WHEN 'DefaultValue' THEN 1
ELSE (SELECT TOP 1 1 FROM atable)
END

SQL Server 2008 execution plan question

I have a question addressed to sql guru.
There are two tables with almost identical structure.
Based on parameter passed into the stored procedure I need to collect data from one or another table.
How to do that in a best way?
Please do not suggest to combine those tables into single one - that is not appropriate.
I did it following (MS SQL Server 2008):
Select *
FROM
String s
JOIN (
SELECT id
,TypeCode
,ProdNo
FROM Table1
WHERE #param = 1 AND TypeCode= 'INV'
UNION
SELECT id
,TypeCode
,ProdNo
FROM Table2
WHERE #param = 2 AND TypeCode= 'INV'
) m ON m.Id = s.Id
WHERE s.Id = 256
but when I looked at execution plan I was surprised because it got data from both tables in parallel threads and only after that filtered by #param value.
I thought that filtering will be made on the first stage and data collected from single table.
Is there a way to make select only from one table without splitting query into two queries and using IF operator?
Thanks
you really need to read this Dynamic Search Conditions in T-SQL by Erland Sommarskog. You shouldn't worry about repeating code, this is not some homework assignment. Just worry about making the execution plan use an index. When making SQL code "pretty" the only thing to consider is indenting & case, any other changes can cause the query plan to be slower. I've seen trivial changes to a super fast query result in a super slow query. GO FOR SPEED (index usage) and duplicate code as necessary. also see: The Curse and Blessings of Dynamic SQL
You tagged the question sql-server-2008, so if you're running SQL 2008 SP1 CU5 (10.0.2746) and SQL 2008 R2 CU1 (10.50.1702) and later, there is a new behavior (as explained in the "Dynamic Search Conditions" article linked above) of OPTION(RECOMPILE) that does not appear in all versions of SQL 2008 or in 2005. This behavior basically evaluates the #Local_Variables values at runtime and recompiles the query accordingly. In your case, this should cause one half of your UNION to be eliminated when compiling them.
Could you just use a simple IF statement?
IF #Param = 1
BEGIN
EXEC SQL
END
ELSE IF #Param = 2
BEGIN
EXEC SQL
END
ELSE
RAISERROR('Invalid Parameter', 16, 1)
Or alternatively you could build the query dynamically and execute it using the sp_executesql stored procedure.
DECLARE #Sql NVARCHAR(100)
SET #Sql = N'SELECT * FROM ('
IF #Param = 1
SET #Sql = #Sql + N'SELECT 1 a, 2 b, 3 c'
ELSE IF #param = 2
SET #Sql = #Sql + N'SELECT 4 a, 5 b, 6 c'
ELSE
RAISERROR('Invalid Parameter', 16, 1)
SET #Sql = #Sql + ') tbl'
EXEC sp_executesql #sql
First thing I'd suggest is putting the ID filter inside the union as well.
I've also changed the UNION to UNION ALL. This avoids evaluating DISTINCT rows
Select *
FROM
String s
JOIN (
SELECT id
,TypeCode
,ProdNo
FROM Table1
WHERE #param = 1 AND TypeCode= 'INV' AND id = 256
UNION ALL
SELECT id
,TypeCode
,ProdNo
FROM Table2
WHERE #param = 2 AND TypeCode= 'INV' AND id = 256
) m ON m.Id = s.Id
WHERE s.Id = 256
SQL Server's not that clever - when writing queries you should only ensure that you send the least amount of SQL to get the data you want (without sending superfluous statements), but also provide the most amount of information (via filters) where possible to give the query optimiser as many hints as possible about the data. As you've seen, it will execute all the SQL you send it.
So it sounds like you need to use dynamic-SQL from what I'm reading. This also gives you the benefit of being able to merge common parts of the SQL cutting down on the amount of duplication. For example, you could have (just taking your inner code -- you can wrap the rest of your stuff around it):
DECLARE #sql NVARCHAR(1000)
SET #sql = 'SELECT id, TypeCode, ProdCode'
IF #param = 1
SET #sql = #sql + ' FROM table1'
IF #param = 2
SET #sql = #sql + ' FROM table2'
SET #sql = #sql + ' WHERE TypeCode = ''INV'''
EXECUTE sp_ExecuteSQL #sql
Just be aware, if you're going to wind this into something more complicated, about little Bobby Tables: it's possible to abuse sp_ExecuteSQL and open gaping holes, but used correctly - with parameterised dynamic SQL - it's as good as a stored procedure.
if you can create a tDUMMY table (with a single dummy row) give this a shot.
Select
*
FROM
String s
JOIN (
SELECT
id, TypeCode, ProdNo
FROM
tDUMMY INNER JOIN Table1 ON TypeCode= 'INV'
WHERE
#param = 1
UNION ALL
SELECT
id, TypeCode, ProdNo
FROM
tDUMMY INNER JOIN Table2 ON TypeCode= 'INV'
WHERE
#param = 2
) m ON m.Id = s.Id
WHERE s.Id = 256
theoretically the query optimizer should first filter the tDUMMY table and then attempt the join. So if #param = 1 the second query should get out much faster (it will check 1 row of tDUMMY again, but it shouldn't check table2)
Note - i also made it UNION ALL (but it wouldn't have much of an impact) because one side will always return no rows anyway.