Dynamically choose table - sql

There are two tables with the same structure (same columns, same column names, etc).
How can I design a query so that a certain table is queried from, depending on a variable?
DECLARE #MYVAR SMALLINT = 0;
DECLARE #TABLENAME VARCHAR(MAX);
SET #TABLENAME = CASE WHEN #MYVAR = 1 THEN 'TABLE1' ELSE 'TABLE2' END
SELECT #TABLENAME
-- HOW TO DYNAMICALLY SELECT TABLE NAME HERE?
SELECT TOP 1 * FROM #TABLENAME
Technet docs hint at maybe using a table alias here, but the examples don't show anything related to this.

As you mentioned, you need to build query dynamically and execute it
DECLARE #MYVAR SMALLINT = 0;
DECLARE #TABLENAME VARCHAR(MAX);
SET #TABLENAME = CASE WHEN #MYVAR = 1 THEN 'TABLE1' ELSE 'TABLE2' END
declare #sql = 'SELECT TOP 1 * FROM '+ quotename(#TABLENAME)
Exec (#sql) -- To execute the query that is built dynamically

Beside the obvious solution with dynamic SQL (which you would need especially if your column lists might not be the same) you can go like this:
DECLARE #tblName VARCHAR(100)='tbl1';
SELECT Col1, Col2, Col3
FROM tbl1
WHERE #tblName='tbl1'
UNION ALL
SELECT Col1, Col2, Col3
FROM tbl2
WHERE #tblName='tbl2'
The biggest advantage was, that this approach is inlineable, can be used as VIEW or better as inline TVF.

If you want to keep it in plain SQL (without the procedural part) it can be done like this:
SELECT * FROM TABLE1 WHERE #MYVAR = 1
UNION ALL
SELECT * FROM TABLE2 WHERE #MYVAR = 0
SQL Server should be smart enough to evaluate one of the conditions as "always false" and skip reading the other table. Consult EXPLAIN to confirm that.

Related

Selecting columns in a table where table name is selected from another table and concatenate them selecting only specific columns

I have a table:
TableName rn
Tab_1 1
Tab_2 2
Tab_3 3
Tab_1, Tab_2 and Tab_3 are tables stored in the database.
What i want is to read all these tables using a loop, select specific columns (say col1, col2, and col3) and concatenate them.
What i tried was:
'''
DECLARE
#table NVARCHAR(128),
#sql NVARCHAR(MAX);
SET #table = N'select tablename from #db2 where rn=1';
SET #sql = N'SELECT * FROM ' + #table;
EXEC sp_executesql #sql;
'''
This query does not exactly concatenate the tables one by one but i am first trying to select the tables dynamically first before i use them in a loop. This does not seem to be working, it returns 'incorrect syntax near 'select''.
#db2 is the temp table that has all the table names.
I have looked at various methods but am not able to figure one out to suit this specific problem.
How do i go about working this out?
You are actually storing SQL select statement into #table by the Set Statement
SET #table = N'select tablename from #db2 where rn=1';
Due to which the final #sql has the statement like "SELECT * FROM select tablename from #db2 where rn=1"
While you need to set the value into #table by
Select #table = db.TableName From #db2 As db With (Nolock) Where db.rn = 1
Try this way, it will work. You need either use While Loop or Cursor for your requirments.

Find total number of data written in two same table from two different databases

I have been trying to write an easy query for my problem. I wrote something but it takes so long when I try to use it for 20 different database which is first I declare an integer than I keep adding the numbers that I summed each other it looks like this;
DECLARE #counter int
SET #counter = (SELECT COUNT(*) as TOTAL FROM database1.dbo.User);
SET #counter = #counter + (SELECT COUNT(*) as TOTAL FROM database2.dbo.User);
SET #counter = #counter + (SELECT COUNT(*) as TOTAL FROM database3.dbo.User);
PRINT #counter;
But I'm looking for an easier way because in my server there is more than 200 databases and I have to choose nearly 35 of them which there names starts same than they have numbers that defines them for example:
database1
database2
But other than that 35 of them are different names so I have to put something in my query that allows me to access the number of users in that database easily.
You could use the undocumented sp_MsForEachDb
The ? is replaced by the DB name.
Try something like:
CREATE TABLE #T (Total INT, DbName SYSNAME)
exec sp_MsForEachDb '
IF EXISTS (SELECT 1 FROM ?.sys.tables WHERE Name = ''Users'')
AND ''?'' LIKE ''Database%''
BEGIN
INSERT INTO #T(Total, DbName)
SELECT count(1) Total, ''?'' DbName
FROM ?.dbo.Users
END
'
SELECT *
FROM #T
--
-- WHERE DbName LIKE '%SOMECONDITION%'
To get the documentation you could execute: EXEC sp_helptext sp_msforeachdb
The following would also give you your desired results without creating a temp table:
declare
#sql varchar(max) = ''
, #maxid int = (select max(database_id) from sys.databases)
select #sql = #sql +
'select '''+name+''' DatabaseName, count(*) [Count] from '+quotename(name)+'.[dbo].[User]'+char(10)+ case when database_id = #maxid then '' else 'union all' end+char(10)
from sys.databases
where name not in ('master','model','msdb','tempdb')
order by database_id
print (#sql)
exec (#sql)
Note: You need to exclude databases that do not have the User Table in the database in the where clause, where I excluded the system databases...
Results will look like this before executing the Dynamic SQL:
For security reasons, I removed the actual Database Names on my machine...

SQL: Replacing part of query by subquery in FROM clause

I have a database including a table called [Table] which lists all tables in that database. Now I want to write a SQL query using some JOINS, which gets a specific tablename from [Table] in a subquery to select from that table... I hope this is not too confusing.
So [Table] looks like this:
IdTable Tablename
1 Adress
2 Project
3 User
...
The query should look like this:
SELECT * FROM (SELECT Type FROM dbo.[Table] WHERE tablename = 12)
Would something like that be possible?
I know, that subqueries are possible, but I do not know how to do that in this case.
Thanks in advance.
Regards
Lars
Try this below dynamic code to get your expected result.
DECLARE #sqlCommand AS NVARCHAR(MAX)
DECLARE #TableName AS NVARCHAR(MAX)
SELECT #TableName = tablename FROM dbo.[Table] WHERE Type = 12
SET #sqlCommand= N'SELECT * FROM '+#TableName+''
EXEC (#sqlCommand)
Note: I guess you wants to select TableName where Type = 12 and I alter the selection and filtering accordingly.
You can also execute the query directly as below without creating the command string-
DECLARE #TableName AS NVARCHAR(MAX)
SELECT #TableName = tablename FROM dbo.[Table] WHERE Type = 12
EXEC (N'SELECT * FROM '+#TableName+'')

drop all SQL tables that appear in a query

I am attempting to develop a script to compare two databases to determine extra tables in one, then delete those tables. Here's my current script to locate the extraneous tables:
-- Any location where TARGET appears, replace TARGET with the database to be
-- modified
-- Any location where MODEL appears, replace MODEL with the database being
-- used as a model for comparison
select 'TARGET' as dbname, t1.table_name
from TARGET.[INFORMATION_SCHEMA].[tables] as t1
where table_name not in (select t2.table_name
from
MODEL.[INFORMATION_SCHEMA].[tables] as t2
)
That gives me the results I need, but now I need to fire out how to drop the tables. I'm afraid I'm utterly lost at this point. Wouldn't mind a way to declare variables instead of typing in the DBname repeatedly either, but not sure I can in this instance.
You could use dynamic SQL:
DECLARE #sql NVARCHAR(MAX) = N'';
select #sql += CONCAT('DROP TABLE ',QUOTENAME(t1.table_name,''''),';',CHAR(13))
from TARGET.[INFORMATION_SCHEMA].[tables] as t1
where table_name not in (select t2.table_name
from MODEL.[INFORMATION_SCHEMA].[tables] as t2);
SELECT #sql; -- debug
--EXEC(#sql);
EDIT:
MySQL(may need some nitpicking):
SET #s = (select GROUP_CONCAT('DROP TABLE ''' + t1.table_name + ''';' SEPARATOR CHAR(13))
from TARGET.INFORMATION_SCHEMA.tables as t1
where table_name not in (select t2.table_name
from MODEL.INFORMATION_SCHEMA.tables as t2));
SELECT #s; -- debug
PREPARE stmt FROM #s;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
My immediate thought is to assign each result in your query a Row Number and place your results into a temp table. Use a while loop starting at 1 and loop through the maximum number you have within that temp table getting the name of the table that Row Number is assigned to each loop. Use that name to delete from the database.
select 'TARGET' as dbname, t1.table_name
, ROW_NUMBER() OVER (Partition By t1.table_name) AS RowNumber
INTO #temp
from TARGET.[INFORMATION_SCHEMA].[tables] as t1
where table_name not in (select t2.table_name
from MODEL.[INFORMATION_SCHEMA].[tables] as t2)
DECLARE #counter INT = 1
DECLARE #maxNum INT
SELECT #maxNum = MAX(RowNumber) FROM #temp
While #counter <= #maxNum
BEGIN
DECLARE #tableName AS VARCHAR(MAX)
SELECT #tableName = table_name FROM #temp WHERE RowNumber = #counter
DELETE TABLE #tableName ' This may not be possible, but follow my lead
#counter += 1
END
DROP TABLE #temp
I am not sure if "DELETE TABLE #tableName" is a proper command but there is probably a very similar solution using what I have given you. I assume this is T-SQL..

SQL Stored Procedure: Conditional Return

Hi I want to create a simple stored proecudre which does the following:
Psudocode
#tempSelect = "SELECT * FROM Table"
if (#Param is NULL) then
exec #tempSelect
else
exec #tempSelect + ' WHERE id = ' + #Param + '
Is this method efficent? Thank you.
Try
select *
from table
where id=isnull(#param, id)
Select * from Table
Where (ID = #Param or #Param is null)
Or
Select * from Table
Where ID=Coalesce(#Param, ID)
[And if you are aiming for efficiency, replace * with the specific field you want to return.]
Yes - I certainly see nothing wrong with it. You could make it even simpler though:
Set NOCOUNT On;
if (#Param is NULL)
Select * From Table;
else
Select * From Table Where (ID=#Param);
Note: I'd probably spell out the fields, though.
Depending on the case, I would probably use dynamic SQL.
However you need to remember about SQL injection in case #param originates from a user, thats why you should never add a parameter directly to your sql.
In t-sql it would look something like (out of my head and untested ;):
DECLARE #SQL NVARCHAR(MAX)
SET #SQL = N'
SELECT ...
FROM table t
WHERE 1 = 1' (
IF(#param IS NOT NULL)
SET #SQL = #SQL + '
AND t.id = #id'
... possibly more things added to the query ...
EXEC sp_executesql
#SQL
, '#id AS INT'
, #id = #Param
By doing this, you will get an optimized query plan for each case (and by using sp_executesql, the query cache will be used as well)
I would especially avoid the OR solution, if you check the query plans generated with the OR compared to one without, you will understand why.
Try this code:
CREATE PROCEDURE [dbo].[ProcedureName]
#Param varchar(50)
AS
BEGIN
declare #tempSelect nvarchar(max)
SET NOCOUNT ON;
set #tempSelect = 'SELECT Col1, Col2 FROM Table where Col1 <> '' '
if #Param <> ''
begin
set #resultSet = #resultSet + ''' and Col1='''+#Param1
end
EXEC(#resultSet)
END