How do i can resolve this promblem? - tsql

I have an table include 1 column ID and 12 columns month (Month01 to Month12). If i have an parameter #month=9, how can i compare to get column Month01 to Month09 without Month10, Month11, Month12. Please help me!

Simo's answer is more elegant, but below is an alternative "basic" way to do this:
DECLARE #month INT
SET #month = 9
IF #Month = 1
BEGIN
SELECT Id, Month01
FROM MonthlyData
END
IF #Month = 2
BEGIN
SELECT Id, Month01, Month02
FROM MonthlyData
END
-- Repeat for months 3-9
IF #Month = 9
BEGIN
SELECT Id, Month01, Month02, Month03, Month04,
Month05, Month06, Month07, Month08, Month09
FROM MonthlyData
END
-- Repeat for months 10-12
See my SQL Fiddle.

declare #str varchar(1000)
SELECT #str= coalesce(#str + ', ', '') + a.name
FROM (
Select name from sys.columns
where object_id = OBJECT_ID('table_months')
and isnumeric(right(name,2))= 1 and name like 'month%'
and cast(right(name,2) as int) < '03') a
-- Instead of '03' you use a variable and assign required moths number
declare #sql nvarchar(100)
set #sql = 'select ID, '+ #str+' from table_months'
exec sp_executesql #sql

you may try this out also :)
DECLARE #month int = 9
DECLARE #Sql nvarchar(100) =''
DECLARE #pos int = 1
WHILE(#pos <= #month)
BEGIN
SET #Sql = #Sql + (',month' + right('00'+ rtrim(#pos), 2))
SET #pos = #pos + 1
END
SET #Sql= 'SELECT ID ' +#Sql + ' FROM table'
EXEC sp_executesql #Sql

DECLARE #month int
DECLARE #tableName nvarchar(128)
SET #month = 9;
SET #tableName = 'months' -- YOUR TABLE's NAME
DECLARE #query nvarchar(2048)
SET #query =
'SELECT id, ' + STUFF((SELECT DISTINCT ', ' + name
FROM sys.columns WHERE name LIKE'Month%'
AND object_id = OBJECT_ID(#tableName)
AND CONVERT(INT, RIGHT(name, 2)) <= #month
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'')
+ ' FROM ' + #tableName;
EXEC(#query)

Related

How to create view for all tables in database?

I want to make views from all tables already exist in database that’s hard task to catch the tables one by one and make create view XXXX as select * from Table_name .I find something that it is possible with cursor and the code is :
DECLARE #TableName sysname
DECLARE #ColumnCount INT
DECLARE #ColumnID INT
DECLARE #SelectColumn NVARCHAR(500)
DECLARE #sql NVARCHAR(max) = ''
DECLARE QUERYINFO CURSOR FOR
SELECT
t.name AS TableName,
ccount.ColumnCount,
c.column_id AS ColumnID,
CASE WHEN c.column_id <> ccount.ColumnCount
THEN c.name + ', '
ELSE c.name
END AS SelectColumn
FROM sys.tables t
INNER JOIN sys.columns c ON t.object_id=c.object_id
INNER JOIN (
SELECT object_id,COUNT(*) AS ColumnCount
FROM sys.columns
GROUP BY object_id
) ccount ON t.object_id = ccount.object_id
ORDER BY t.Name,c.column_id
OPEN QUERYINFO
FETCH NEXT FROM QUERYINFO INTO #TableName,#ColumnCount,#ColumnID,#SelectColumn
WHILE ##FETCH_STATUS = 0
BEGIN
IF #ColumnID = 1
BEGIN
SET #sql = 'CREATE VIEW v_' + #TableName + ' AS SELECT ' + #SelectColumn
END
ELSE
BEGIN
SET #sql = #sql + #SelectColumn
END
IF #ColumnID = #ColumnCount
BEGIN
SET #sql = #sql + ' FROM ' + #TableName
EXEC sys.sp_executesql #sql
SET #sql = ''
END
FETCH NEXT FROM QUERYINFO INTO #TableName,#ColumnCount,#ColumnID,#SelectColumn
END
CLOSE QUERYINFO
DEALLOCATE QUERYINFO
but it has error and I don't know how solve it
This is what I would do with dynamic SQL; checking first to see if the view exists, then creating it with all of the columns (not by using SELECT * FROM...).
DECLARE #SQL nvarchar(MAX)
SET #SQL = N''
SELECT
#SQL = #SQL +
N'IF EXISTS(SELECT 1 FROM sys.objects WHERE name = N''v_' + t.name + N''' AND type = N''V'') BEGIN DROP VIEW [v_' + t.name + '] END CREATE VIEW [v_' + t.name + N'] AS SELECT ' +
STUFF(
(SELECT N',' + c.name
FROM
sys.columns AS c
WHERE
c.OBJECT_ID = t.OBJECT_ID
ORDER BY
column_id
FOR XML PATH(''), TYPE).value('.',N'nvarchar(max)')
,1,1,N'')
+ N' FROM [' + t.name + N'];'
FROM
sys.tables AS t
EXEC sp_executesql #SQL

Altering Multiple Tables at once

I'm trying to alter multiple SQL Server 2008 R2 tables at one time.
This is my code:
use DatabaseName
go
Declare #SchemaUsed varchar(20) = 'dbo'
create table #Tables
(
TableName varchar(100), Processed int
)
insert into #Tables
select top 1 table_name, 0
from INFORMATION_SCHEMA.TABLES
where TABLE_SCHEMA = #SchemaUsed
and table_type = 'Base Table'
and (TABLE_NAME like 'PM%' )
ORDER BY TABLE_NAME
DECLARE #TableName varchar(max)
DECLARE #SQL varchar(max)
WHILE EXISTS (select top 1 'x' from #Tables where Processed = 0)
BEGIN
SET #TableName = (select top 1 TableName from #Tables where Processed = 0)
Set #SQL = 'ALTER TABLE ' + #SchemaUsed + '.' + #TableName + ' ADD [identityID] bigint IDENTITY(1, 1) NOT NULL '
-- Set #SQL = '''' + #SQL + ''''
Print #SQL
EXEC #SQL;
update #Tables
set Processed = 1
where TableName = #TableName
END
drop table #Tables
I can't get this to work to save my life and get the following error:
Lookup Error - SQL Server Database Error: The name 'ALTER TABLE
dbo.PM1GTVLV ADD [identityID] bigint IDENTITY(1, 1) NOT NULL ' is not
a valid identifier.
I've also tried multiple string variations and using sp_executesql as well.
Can someone point out where I've gone wrong?
Try
DECLARE #SQL NVARCHAR(MAX);
EXEC sp_executesql #SQL;
Instead of EXEC #sql.
As an aside, this is a much more usable version of the same code IMHO:
DECLARE #SchemaUsed VARCHAR(20) = 'dbo';
DECLARE #sql NVARCHAR(MAX) = N'';
SELECT #sql += CHAR(13) + CHAR(10) + N'ALTER TABLE '
+ QUOTENAME(#SchemaUsed) + '.'
+ QUOTENAME(name) + ' ADD [identityID]
BIGINT IDENTITY(1,1) NOT NULL;'
FROM sys.tables
WHERE SCHEMA_NAME([schema_id]) = #SchemaUsed
AND name LIKE 'PM%';
PRINT #sql;
--EXEC sp_executesql #sql;
Or even better:
DECLARE #SchemaUsed VARCHAR(20) = 'dbo';
DECLARE #sql NVARCHAR(MAX) = N'';
SELECT #sql += CHAR(13) + CHAR(10) + N'ALTER TABLE '
+ QUOTENAME(#SchemaUsed) + '.'
+ QUOTENAME(name) + ' ADD [identityID]
BIGINT IDENTITY(1,1) NOT NULL;'
FROM sys.tables AS t
WHERE SCHEMA_NAME([schema_id]) = #SchemaUsed
AND name LIKE 'PM%'
AND NOT EXISTS (SELECT 1 FROM sys.columns AS c
WHERE [object_id] = t.[object_id]
AND c.is_identity = 1);
PRINT #sql;
--EXEC sp_executesql #sql;
To execute a character string, EXEC requires parenthesis around the string (or character variable) as shown in the BOL syntax:
EXEC (#SQL);

Dynamic SQL reads a local variable as a Table Variable?

This is NOT about using a table variable - this is about using a local variable to carry a db address within a dynamic SQL cursor which theoretically would work as follows:
-- Assume the global variables #sql, AnalysisLocation, and #sp_executeSql have been declared.
ALTER PROCEDURE [dbo].[sp_AggregateCompliance_Report]
#clientID int,
#InvScrDBLocation nvarchar(250),
#JoinFilter nvarchar(max) = '',
#Criteria nvarchar(max) = '',
#Year int = NULL
as
declare #sql nvarchar(4000)
set #sql = '
IF EXISTS (SELECT * FROM sys.tables WHERE name = ''tmp_Aggregate_Compliance_counts'')
TRUNCATE TABLE tmp_Aggregate_Compliance_counts
ELSE
CREATE TABLE tmp_Aggregate_Compliance_counts (
pfc_fk_prv_pkid int,
RxYear int,
RxMonth int,
Compliance decimal (6,5))
' print #sql EXEC sp_executesql #sql
SET #Criteria = isnull(case when #Criteria like 'WHERE %' then 'AND '+substring(#criteria,7,len(#criteria)-6) else #Criteria end ,'')
SET #Year = isnull(#year, year(getdate())-1)
set #sql = '
DECLARE #fk_cli_pkid INT
, #ServerAndDB_for_pfcAppended nvarchar(100)
DECLARE client_set CURSOR FOR
SELECT DISTINCT mtx.fk_cli_pkid, SettingValue+ ''.dbo.pfc_appended''
FROM mtx_ComplianceAndEarlyRefill_tracking AS mtx
JOIN prola7.Invoice_Screens.dbo.client_definition AS def
ON mtx.fk_cli_pkID = def.fk_cli_pkid
AND fk_lkSettings_pkID = 45
AND RecordStatus = 1
OPEN client_set
FETCH next FROM client_set
INTO #fk_cli_pkid, #ServerAndDB_for_pfcAppended
WHILE ##FETCH_STATUS = 0 BEGIN
INSERT INTO tmp_Aggregate_Compliance_counts (pfc_fk_prv_pkid, RxYear, RxMonth, Compliance)
SELECT pfc.pfc_fk_prv_pkid
, year(mtx.pfc_dateofservice) AS RxYear
, 0 AS RxMonth
, cast(mtx.Compliance as decimal (6,5))
FROM mtx_ComplianceAndEarlyRefill_tracking AS mtx
LEFT OUTER JOIN #ServerAndDB_for_pfcAppended AS pfc
ON mtx.pp_clientfile = pfc.pp_clientfile
AND mtx.pp_mirror_pkid = pfc.pp_mirror_pkid
AND mtx.fk_cli_pkid = #fk_cli_pkid
'+#JoinFilter+'
WHERE pfc.pfc_status = 0
AND year(mtx.pfc_dateofservice) = '+cast(#Year as nvarchar)+'
'+#Criteria+'
GROUP BY pfc.pfc_fk_prv_pkid, year(mtx.pfc_dateofservice)
FETCH next FROM client_set
INTO #fk_cli_pkid, #ServerAndDB_for_pfcAppended
END
CLOSE client_set
DEALLOCATE client_set
' print #sql EXEC sp_executesql #sql
This creates no syntax errors when compiling the dynamic code, however when calling this procedure: Msg 1087, Level 15, State 2, Line 27
Must declare the table variable "#ServerAndDB_for_pfcAppended".
When I use this type of structure passing the location variable in as a global variable from outside the procedure it accepts it correctly, however as a local variable it seems to default to presuming I intend it to be a table variable.
I do NOT want to create a table variable. Is this an impossible structure?
The error is caused by the fact that you are attempting to have a parametrised table name. This is not possible, and whenever a table name should be a parameter, a dynamic query is used, basically like this:
SET #sql = 'SELECT … FROM ' + #tablename + ' WHERE …'
I think, in your situation the cursor should be taken out of the dynamic query, except for the part that uses the parametrised table name. Something like this should probably do:
ALTER PROCEDURE [dbo].[sp_AggregateCompliance_Report]
#clientID int,
#InvScrDBLocation nvarchar(250),
#JoinFilter nvarchar(max) = '',
#Criteria nvarchar(max) = '',
#Year int = NULL
as
declare #sql nvarchar(4000)
set #sql = '
IF EXISTS (SELECT * FROM sys.tables WHERE name = ''tmp_Aggregate_Compliance_counts'')
TRUNCATE TABLE tmp_Aggregate_Compliance_counts
ELSE
CREATE TABLE tmp_Aggregate_Compliance_counts (
pfc_fk_prv_pkid int,
RxYear int,
RxMonth int,
Compliance decimal (6,5))
' print #sql EXEC sp_executesql #sql
SET #Criteria = isnull(case when #Criteria like 'WHERE %' then 'AND '+substring(#criteria,7,len(#criteria)-6) else #Criteria end ,'')
SET #Year = isnull(#year, year(getdate())-1)
DECLARE #fk_cli_pkid INT
, #ServerAndDB_for_pfcAppended nvarchar(100)
DECLARE client_set CURSOR FOR
SELECT DISTINCT mtx.fk_cli_pkid, SettingValue+ ''.dbo.pfc_appended''
FROM mtx_ComplianceAndEarlyRefill_tracking AS mtx
JOIN prola7.Invoice_Screens.dbo.client_definition AS def
ON mtx.fk_cli_pkID = def.fk_cli_pkid
AND fk_lkSettings_pkID = 45
AND RecordStatus = 1
OPEN client_set
FETCH next FROM client_set
INTO #fk_cli_pkid, #ServerAndDB_for_pfcAppended
WHILE ##FETCH_STATUS = 0 BEGIN
set #sql = '
INSERT INTO tmp_Aggregate_Compliance_counts (pfc_fk_prv_pkid, RxYear, RxMonth, Compliance)
SELECT pfc.pfc_fk_prv_pkid
, year(mtx.pfc_dateofservice) AS RxYear
, 0 AS RxMonth
, cast(mtx.Compliance as decimal (6,5))
FROM mtx_ComplianceAndEarlyRefill_tracking AS mtx
LEFT OUTER JOIN #ServerAndDB_for_pfcAppended AS pfc
ON mtx.pp_clientfile = pfc.pp_clientfile
AND mtx.pp_mirror_pkid = pfc.pp_mirror_pkid
AND mtx.fk_cli_pkid = #fk_cli_pkid
'+#JoinFilter+'
WHERE pfc.pfc_status = 0
AND year(mtx.pfc_dateofservice) = '+cast(#Year as nvarchar)+'
'+#Criteria+'
GROUP BY pfc.pfc_fk_prv_pkid, year(mtx.pfc_dateofservice)
' print #sql EXEC sp_executesql #sql
FETCH next FROM client_set
INTO #fk_cli_pkid, #ServerAndDB_for_pfcAppended
END
CLOSE client_set
DEALLOCATE client_set

T-SQL How To Count Of Records For Each Column

I have a table with over 120 columns and need to determine which column is used the least. I tried using sql queries to do this, but found T-SQL a bit simpler.
I tried the following but my count comes out as 0 for every column.
Declare data1 Cursor for select column_name
from information_schema.columns
where table_name = 'repository'
Declare #mField nvarchar(255)
Declare #count int
Open data1;
fetch next from data1 into #mField;
set #count = -1;
while ##fetch_status = 0
begin
select #count = count(#mField)
from repository where tablereference =
'central' and ( #mField!= null )
print #mField+' ' ;
print #count;
Fetch next from data1 into #mField;
end
close data1;
deallocate data1;
You can't count values like this because you are only testing if #mField is NULL. The column name isn't substituted.
COUNT ignores NULLs anyway so if you want to count non-null values, do this:
DECLARE #sql varchar(4000)
SET #sql = 'SELECT COUNT(*) AS Total '
SELECT #sql = #sql + ', COUNT(' + QUOTENAME(column_name) + ') AS ' + QUOTENAME(column_name)
from information_schema.columns
where table_name = 'repository'
SET #sql = #sql + ' FROM repository'
EXEC (#sql)
This queries the table once for all columns
You need to use some dynamic sql in the middle to acheive your aim here.
Declare data1 Cursor for select column_name
from information_schema.columns
where table_name = 'repository'
Declare #mField nvarchar(255)
Open data1;
fetch next from data1 into #mField;
while ##fetch_status = 0
begin
exec ('
declare #count int
select #count = count([' + #mField + '])
from repository where tablereference =
''central'' and ( [' + #mField + '] is not null)
if #count < 10
begin
print ''' + #mField + ' '' ;
print #count;
end
')
Fetch next from data1 into #mField;
end
close data1;
deallocate data1;
Your count(#mField) is a count of the litteral value that happens to be in #mField, its not resolving the field name into COUNT(fldBlah), you would need to use dynamic SQL for that.
Declare data1 Cursor FAST_FORWARD for select column_name
from information_schema.columns where table_name = 'repository'
Declare #mField nvarchar(255)
Declare #SQL varchar(1024)
Declare #results table (col_name varchar(128), non_nulls int)
Open data1;
fetch next from data1 into #mField;
while (##FETCH_STATUS = 0) begin
set #SQL = 'SELECT ''' + #mField + ''', count(' + #mField + ') from repository where tablereference = ''central'''
insert #results
exec(#SQL)
Fetch next from data1 into #mField;
end
close data1;
deallocate data1;
select * from #results
For an output like;
col_name non_nulls
[tablereference] 5
[another_col] 1
Where there are 5 non-null values in column tablereference etc
You need to use IS NOT NULL instead of != NULL

How to get sp_executesql result into a variable?

I have a piece of dynamic SQL I need to execute, I then need to store the result into a variable.
I know I can use sp_executesql but can't find clear examples around about how to do this.
If you have OUTPUT parameters you can do
DECLARE #retval int
DECLARE #sSQL nvarchar(500);
DECLARE #ParmDefinition nvarchar(500);
DECLARE #tablename nvarchar(50)
SELECT #tablename = N'products'
SELECT #sSQL = N'SELECT #retvalOUT = MAX(ID) FROM ' + #tablename;
SET #ParmDefinition = N'#retvalOUT int OUTPUT';
EXEC sp_executesql #sSQL, #ParmDefinition, #retvalOUT=#retval OUTPUT;
SELECT #retval;
But if you don't, and can not modify the SP:
-- Assuming that your SP return 1 value
create table #temptable (ID int null)
insert into #temptable exec mysp 'Value1', 'Value2'
select * from #temptable
Not pretty, but works.
DECLARE #vi INT
DECLARE #vQuery NVARCHAR(1000)
SET #vQuery = N'SELECT #vi= COUNT(*) FROM <TableName>'
EXEC SP_EXECUTESQL
#Query = #vQuery
, #Params = N'#vi INT OUTPUT'
, #vi = #vi OUTPUT
SELECT #vi
DECLARE #tab AS TABLE (col1 VARCHAR(10), col2 varchar(10))
INSERT into #tab EXECUTE sp_executesql N'
SELECT 1 AS col1, 2 AS col2
UNION ALL
SELECT 1 AS col1, 2 AS col2
UNION ALL
SELECT 1 AS col1, 2 AS col2'
SELECT * FROM #tab
Return values are generally not used to "return" a result but to return success (0) or an error number (1-65K). The above all seem to indicate that sp_executesql does not return a value, which is not correct. sp_executesql will return 0 for success and any other number for failure.
In the below, #i will return 2727
DECLARE #s NVARCHAR(500)
DECLARE #i INT;
SET #s = 'USE [Blah]; UPDATE STATISTICS [dbo].[TableName] [NonExistantStatisticsName];';
EXEC #i = sys.sp_executesql #s
SELECT #i AS 'Blah'
SSMS will show this
Msg 2727, Level 11, State 1, Line 1
Cannot find index 'NonExistantStaticsName'.
If you want to return more than 1 value use this:
DECLARE #sqlstatement2 NVARCHAR(MAX);
DECLARE #retText NVARCHAR(MAX);
DECLARE #ParmDefinition NVARCHAR(MAX);
DECLARE #retIndex INT = 0;
SELECT #sqlstatement = 'SELECT #retIndexOUT=column1 #retTextOUT=column2 FROM XXX WHERE bla bla';
SET #ParmDefinition = N'#retIndexOUT INT OUTPUT, #retTextOUT NVARCHAR(MAX) OUTPUT';
exec sp_executesql #sqlstatement, #ParmDefinition, #retIndexOUT=#retIndex OUTPUT, #retTextOUT=#retText OUTPUT;
returned values are in #retIndex and #retText
Declare #variable int
Exec #variable = proc_name
DECLARE #ValueTable TABLE
(
Value VARCHAR (100)
)
SELECT #sql = N'SELECT SRS_SizeSetDetails.'+#COLUMN_NAME+' FROM SRS_SizeSetDetails WHERE FSizeID = '''+#FSizeID+''' AND SRS_SizeSetID = '''+#SRS_SizeSetID+'''';
INSERT INTO #ValueTable
EXEC sp_executesql #sql;
SET #Value='';
SET #Value = (SELECT TOP 1 Value FROM #ValueTable)
DELETE FROM #ValueTable
This worked for me:
DECLARE #SQL NVARCHAR(4000)
DECLARE #tbl Table (
Id int,
Account varchar(50),
Amount int
)
-- Lots of code to Create my dynamic sql statement
insert into #tbl EXEC sp_executesql #SQL
select * from #tbl
Here's something you can try
DECLARE #SqlStatement NVARCHAR(MAX) = ''
,#result XML
,#DatabaseName VARCHAR(100)
,#SchemaName VARCHAR(10)
,#ObjectName VARCHAR(200);
SELECT #DatabaseName = 'some database'
,#SchemaName = 'some schema'
,#ObjectName = 'some object (Table/View)'
SET #SqlStatement = '
SELECT #result = CONVERT(XML,
STUFF( ( SELECT *
FROM
(
SELECT TOP(100)
*
FROM ' + QUOTENAME(#DatabaseName) +'.'+ QUOTENAME(#SchemaName) +'.' + QUOTENAME(#ObjectName) + '
) AS A1
FOR XML PATH(''row''), ELEMENTS, ROOT(''recordset'')
), 1, 0, '''')
)
';
EXEC sp_executesql #SqlStatement,N'#result XML OUTPUT', #result = #result OUTPUT;
SELECT DISTINCT
QUOTENAME(r.value('fn:local-name(.)', 'VARCHAR(200)')) AS ColumnName
FROM #result.nodes('//recordset/*/*') AS records(r)
ORDER BY ColumnName
This was a long time ago, so not sure if this is still needed, but you could use ##ROWCOUNT variable to see how many rows were affected with the previous sql statement.
This is helpful when for example you construct a dynamic Update statement and run it with exec. ##ROWCOUNT would show how many rows were updated.
Here is the definition