Enabling or disabling part of TSQL select statement (columns) - tsql

Let's say I have a table with five columns. Full select statement would be :
Select col1, col2, col3, col4, col5
from tbl
But I need this query to be "dynamic", every column must be possible to enable/disable. So, as far as I understand I need five tags (bool let's say).
If i have tag1=0, tag2=1, tag3=1, tag4=0, tag5 =0 then the select statement should be like this:
Select col2, col3
from tbl
If i have tag1=1, tag2=0, tag3=0, tag4=0, tag5 =0 then the select statement should be like this:
Select col1
from tbl
So is there a possibility to do so in TSQL? I intend to create SP and execute it from php.
P.s. I understand that there's a solution to make a bunch of IF statements with all possible tag1...tag5 variations, but this is not very effective when the number of columns (and enable tags) is high.

create procedure GetTblList
#Tag1 bit,
#Tag2 bit,
#Tag3 bit,
#Tag4 bit,
#Tag5 bit
as
declare #SQL nvarchar(max)
set #SQL = 'select '
if #Tag1 = 1 set #SQL = #SQL + 'Col1,'
if #Tag2 = 1 set #SQL = #SQL + 'Col2,'
if #Tag3 = 1 set #SQL = #SQL + 'Col3,'
if #Tag4 = 1 set #SQL = #SQL + 'Col4,'
if #Tag5 = 1 set #SQL = #SQL + 'Col5,'
if #SQL = 'select ' set #SQL = #SQL + 'null as NoColumnSelected'
set #SQL = stuff(#SQL, len(#SQL), 1, ' from TbL')
exec (#SQL)

Even tough it is not a recommended practice the only possible way seems to be creating the sql statement as a string and executing it.
If you build the sql statement string in your application layer you can send it to db layer for execution as a single string. This one should be clear.
On the other hand if you want build the sql statement string at DB level (in an SP for example) you can use sp_executesql stored procedure to execute it. Sample:
DECLARE #sql nvarchar(MAX);
SET #sql = 'SELECT col1'
IF #tag1 = 1 SET #sql = #sql + ', dynamic_col_1';
IF #tag2 = 1 SET #sql = #sql + ', dynamic_col_2';
IF #tag3 = 1 SET #sql = #sql + ', dynamic_col_3';
SET #sql = #sql + ' FROM myTable'
EXEC sp_executesql #sql;
Either case you need to send all your tags as parameters or you can aggregate them into a single 5 digit string parameter where each digit represents a flag.

Related

How to Get output from nested SP_executeSQL

Can you tell me how to insert executed variable #name in my table?
I had some coding and this is what i managed to do but I do not know whats next:
DECLARE #Name nvarchar(200);
DECLARE #dbcatalog nvarchar(128);
declare #sql nvarchar(4000)
select #name = N' select ID from ' + #DbCatalog + '.dbo.Table2 ';
SET #sql = 'insert into Table2(Name) values (#name)'
exec Sp_executeSQL #sql
Are you trying to copy ID values from one table into another? If so, then:
INSERT INTO Table2 (Name)
EXEC(#Name)

An expression of non-boolean type specified in a context where a condition is expected, near 'tblProje'

I am getting this error message when running this sql statement in ssms:
An expression of non-boolean type specified in a context where a condition is expected, near 'tblProje'
This is the statement itself:
PRINT 'Updating FileSetId data from Table Project to Table tblProject'
DECLARE #SQL NVARCHAR(100)
SET #SQL = 'UPDATE tblProject set tblProject.ProjectFileSetId = Project.FileSetId
FROM Project
WHERE tblProject.AccountingProject = Project.Project_Id'
IF EXISTS(select * from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'Project' AND COLUMN_NAME = 'FileSetId')
BEGIN
execute sp_executesql #SQL
END
GO
I am trying to make the statement so that it can run as many times as possible. Basically checking to make sure that a column exists before trying to update from it. I cannot tell where this error is coming from
If you take your query and look at the length, you will see an issue:
DECLARE #SQL NVARCHAR(100)
SET #SQL = 'UPDATE tblProject set tblProject.ProjectFileSetId = Project.FileSetId
FROM Project
WHERE tblProject.AccountingProject = Project.Project_Id'
select len(#sql)
select len('UPDATE tblProject set tblProject.ProjectFileSetId = Project.FileSetId
FROM Project
WHERE tblProject.AccountingProject = Project.Project_Id')
Your variable assignment is cutting off 41 chars at the end so it is not valid SQL to execute when you run sp_executesql. Change your variable to something like NVARCHAR(4000) or NVARCHAR(MAX) and it will work.
PRINT 'Updating FileSetId data from Table Project to Table tblProject'
DECLARE #SQL NVARCHAR(MAX)
SET #SQL = 'UPDATE tblProject set tblProject.ProjectFileSetId = Project.FileSetId
FROM Project
WHERE tblProject.AccountingProject = Project.Project_Id'
IF EXISTS(select * from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'Project' AND COLUMN_NAME = 'FileSetId')
BEGIN
execute sp_executesql #SQL
END
GO

How to select top 10 rows from information.schema.columns list

SELECT TOP 10
*
FROM
(SELECT
COLUMN_NAME, TABLE_NAME
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
COLUMN_NAME LIKE '%MAIL%')
I am trying to get the top 10 rows from each of the tables within my search. Any ideas? SQL Server 2008 R2
While nscheaffer's answer is valid, I feel obligated to tell you that you can get the exact same functionality without using a cursor, and also while using a query which might be a bit easier to implement.
Just concatenate all of the possible queries together based off the system tables and then execute them simultaneously, like this:
DECLARE #SQL NVARCHAR(MAX)
;
SELECT #SQL =
(
SELECT 'SELECT TOP 10 * FROM ' + OBJECT_NAME(C.Object_ID) + ';' + CHAR(10)
FROM sys.Columns C
INNER JOIN sys.Tables T
ON C.Object_ID = T.Object_ID
AND T.is_ms_shipped = 0
WHERE C.Name LIKE '%Mail%'
GROUP BY C.Object_ID
ORDER BY C.Object_ID
FOR XML PATH('')
)
;
EXEC sp_ExecuteSQL #SQL
;
If you want to check the SQL before it runs, just comment out the EXEC command and replace it with a SELECT, like this:
SELECT #SQL;
--EXEC sp_ExecuteSQL #SQL
;
I would use a cursor to create dynamic SQL and then execute that SQL. Does this work for you?
DECLARE #SQL NVARCHAR (4000) = ''
DECLARE #Table_Name NVARCHAR(50)
DECLARE TableCursor CURSOR FAST_FORWARD READ_ONLY FOR
SELECT DISTINCT TABLE_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME LIKE '%MAIL%'
OPEN TableCursor
FETCH NEXT FROM TableCursor INTO #Table_Name
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQL = #SQL + '
SELECT TOP 10 * FROM ' + #Table_Name + '
GO
'
FETCH NEXT FROM TableCursor INTO #Table_Name
END
CLOSE TableCursor
DEALLOCATE TableCursor
EXECUTE sp_executeSQL #SQL

Dynamic Query w/ Quotes issue

I'm trying to write a dynamic query that produces the same results as the following, but replaces the fixed tablename with a variable.
SELECT *
WHERE tableName = 'Table2A'
works fine but
DECLARE #tablename AS NVARCHAR(100)
SET #tablename = N'Table2A'
DECLARE #execquery AS NVARCHAR(MAX)
SET #execquery = N'
SELECT *
WHERE tableName = ''' + QUOTENAME(#tablename) + N''''
EXECUTE sp_executesql #execquery
returns no records. What am I doing wrong?
Aside from the fact that your SQL statement is invalid, QUOTENAME() simply places brackets "[]" around the supplied variable. Replace the EXECUTE statement with a PRINT statement, and you'll get the following results:
DECLARE #tablename AS NVARCHAR(100)
SET #tablename = N'Table2A'
DECLARE #execquery AS NVARCHAR(MAX)
SET #execquery = N'
SELECT *
WHERE tableName = ''' + QUOTENAME(#tablename) + N''''
PRINT #execquery
--EXECUTE sp_executesql #execquery
RESULTS:
SELECT *
WHERE tableName = '[Table2A]'

How to get row count from EXEC() in a TSQL SPROC?

I have a TSQL sproc that builds a query as and executes it as follows:
EXEC (#sqlTop + #sqlBody + #sqlBottom)
#sqlTop contains something like SELECT TOP(x) col1, col2, col3...
TOP(x) will limit the rows returned, so later I want to know what the actual number of rows in the table is that match the query.
I then replace #sqlTop with something like:
EXEC ('SELECT #ActualNumberOfResults = COUNT(*) ' + #sqlBody)
I can see why this is not working, and why a value not declared error occurs, but I think it adequately describes what I'm trying to accomplish.
Any ideas?
use sp_executesql and an output parameter
example
DECLARE #sqlBody VARCHAR(500),#TableCount INT, #SQL NVARCHAR(1000)
SELECT #sqlBody = 'from sysobjects'
SELECT #SQL = N'SELECT #TableCount = COUNT(*) ' + #sqlBody
EXEC sp_executesql #SQL, N'#TableCount INT OUTPUT', #TableCount OUTPUT
SELECT #TableCount
GO
You could instead have the dynamic query return the result as a row set, which you would then insert into a table variable (could be a temporary or ordinary table as well) using the INSERT ... EXEC syntax. Afterwards you can just read the saved value into a variable using SELECT #var = ...:
DECLARE #rowcount TABLE (Value int);
INSERT INTO #rowcount
EXEC('SELECT COUNT(*) ' + #sqlBody);
SELECT #ActualNumberOfResults = Value FROM #rowcount;
Late in the day, but I found this method much simpler:
-- test setup
DECLARE #sqlBody nvarchar(max) = N'SELECT MyField FROM dbo.MyTable WHERE MyOtherField = ''x''';
DECLARE #ActualNumberOfResults int;
-- the goods
EXEC sp_executesql #sqlBody;
SET #ActualNumberOfResults = ##ROWCOUNT;
SELECT #ActualNumberOfResults;
After executing your actual query store the result of ##ROWCOUNT in any variable which you can use later.
EXEC sp_executesql 'SELECT TOP 10 FROM ABX'
SET #TotRecord = ##ROWCOUNT into your variable for later use.
Keep in mind that dynamic SQL has its own scope. Any variable declared/modified there will go out of scope after your EXEC or your sp_executesql.
Suggest writing to a temp table, which will be in scope to your dynamic SQL statement, and outside.
Perhaps put it in your sqlBottom:
CREATE TABLE ##tempCounter(MyNum int);
EXEC('SELECT #ActualNumberOfResults = COUNT(*) ' + #sqlBody +
'; INSERT INTO ##tempCounter(MyNum) VALUES(#ActualNumberOfResults);');
SELECT MyNum FROM ##tempCounter;
You can use output variable in SP_EXECUTESQL
DECLARE #SQL NVARCHAR(MAX);
DECLARE #ParamDefinition NVARCHAR(100) = '#ROW_SQL INT OUTPUT'
DECLARE #AFFECTED_ROWS INT;
SELECT
#SQL = N'SELECT 1 UNION ALL SELECT 2'
SELECT #SQL += 'SELECT #ROW_SQL = ##ROWCOUNT;';
EXEC SP_EXECUTESQL #SQL, #ParamDefinition, #ROW_SQL=#AFFECTED_ROWS OUTPUT;
PRINT 'Number of affected rows: ' + CAST(#AFFECTED_ROWS AS VARCHAR(20));
Ouput:
SQL2.sql: Number of affected rows: 2
Thanks Jesus Fernandez!
The only problem with the answers that create temporary tables (either using "DECLARE #rowcount TABLE" or "CREATE TABLE ##tempCounter(MyNum int)") is that you're having to read all the affected records off disk into memory. If you're expecting a large number of records this may take some time.
So if the answer is likely to be large the "use sp_executesql and an output parameter" solution is a more efficient answer. And it does appear to work.