Assume I have a table with 50 columns with name AMT_0,AMT_1,..AMT_49. Is there possible sum of dynamic number of column depending on Variable.
example1: SELECT SUM(AMT_0,AMT_1,AMT_2,AMT_3) FROM TBL where the Variable is equal to 4.
example2: SELECT SUM(AMT_0,AMT_1,..,AMT_23) FROM TBL where the Variable is equal to 24.
Ideally, you should redesign your database, so that AMT is normalized into rows. Then the query is far simpler and does not require dynamic SQL.
The only way to do this as it stands is with dynamic SQL, you need to make sure to check all columns exists by looking them up in system tables, and quote all the column names properly:
DECLARE #sql nvarchar(max) = N'
SELECT SUM(
' +
(
SELECT STRING_AGG(QUOTENAME(c.name), N',')
FROM sys.columns c
WHERE c.object_id = OBJECT_ID(N'TBL')
AND c.name LIKE N'AMT_%'
) + N'
)
FROM TBL
';
EXEC (#sql)
You'll need to build some dynamic SQL. Something like this should do the trick
DECLARE #var smallint;
SELECT #var = 15; -- change variable value to control # of columns summed
DECLARE #sql varchar(4000);
DECLARE #cntr smallint; -- incremental counter variable for WHILE loop
SELECT #sql = '';
SELECT #cntr = 1;
WHILE #cntr > 0 AND #cntr <= 50 AND #cntr <= #var
BEGIN
IF #sql = ''
BEGIN
SELECT #sql = 'SELECT SUM(AMT_0';
END
ELSE
SQL #sql += ' + AMT_' + CONVERT(varchar(10), (#cntr-1));
END
SELECT #cntr += 1;
END
SELECT #sql += ') FROM TBL;';
EXEC (#sql);
Related
I have tried a bunch of different ways like using cursors and dynamic SQL, but is there a fast way to verify that all columns in a given table can convert from varchar to float (without altering the table)?
I want to get a print out of which columns fail and which columns pass.
I am trying this method now but it is slow and cannot get the list of columns that pass or error out.
drop table users;
select *
into users_1
from users
declare #cols table (i int identity, colname varchar(100))
insert into #cols
select column_name
from information_schema.COLUMNS
where TABLE_NAME = 'users'
and COLUMN_NAME not in ('ID')
declare #i int, #maxi int
select #i = 1, #maxi = MAX(i) from #cols
declare #sql nvarchar(max)
while(#i <= #maxi)
begin
select #sql = 'alter table users_1 alter column ' + colname + ' float NULL'
from #cols
where i = #i
exec sp_executesql #sql
select #i = #i + 1
end
I found this code on one of the SQL tutorials sites.
Why all the drop/create/alter nonsense? If you just want to know if a column could be altered, why leave your table in a wacky state, where the columns that can be altered are altered, and the ones that can't just raise errors?
Here's one way to accomplish this with dynamic SQL (and with some protections):
DECLARE #tablename nvarchar(513) = N'dbo.YourTableName';
IF OBJECT_ID(#tablename) IS NOT NULL
BEGIN
DECLARE #sql nvarchar(max) = N'SELECT ',
#tmpl nvarchar(max) = N'[Can $colP$ be converted?]
= CASE WHEN EXISTS
(
SELECT 1 FROM ' + #tablename + N'
WHERE TRY_CONVERT(float, COALESCE($colQ$,N''0'')) IS NULL
)
THEN ''No, $colP$ cannot be coverted''
ELSE ''Yes, $colP$ CAN be converted'' END';
SELECT #sql += STRING_AGG(
REPLACE(REPLACE(#tmpl, N'$colQ$',
QUOTENAME(name)), N'$colP$', name), N',')
FROM sys.columns
WHERE object_id = OBJECT_ID(#tablename)
AND name <> N'ID';
EXEC sys.sp_executesql #sql;
END
Working db<>fiddle
This is never going to be "fast" - there is no great shortcut to having to read and validate every value in the table.
I am trying to write a SQL query that adds a certain amount of empty columns, based on the number of rows in a related table (t1) for a Crystal Report. These columns should have the header of the name of the dataset.
So it should look something like this:
However I would need to change the script each time a new row gets added (e.g. opening a store - not very often, but it does happen).
I thought about using the pivot function, but I believe the number of rows must be defined - plus, there is no calculation / aggregation happening.
Does anybody have an idea on how to solve this?
As Larnu already mentioned, dynamic SQL would be one way to go. I would suggest using a combination of XML PATH and dynamic SQL. Following an example:
DECLARE #colList VARCHAR(MAX) = (SELECT STUFF((SELECT ',NULL as t1_row' + cast(col1 AS varchar(3))
FROM MyTable
FOR XML PATH('')) ,1,1,'') AS Txt
)
DECLARE #stmt VARCHAR(MAX) = 'SELECT Col1, Col2, Col3, ' + #colList + ' FROM MyTable'
EXEC (#stmt)
I was able to achieve the result using dynamic SQL.
The Script looks something like this:
DECLARE #STRSQL NVARCHAR(MAX) = 'WITH a AS (SELECT ';
DECLARE #Kst nvarchar(6);
DECLARE #Markt NVARCHAR(30);
DECLARE #SCHEMA_NAME VARCHAR(50) = 'XTRADE';
DECLARE C1 CURSOR FOR
SELECT NUMMER, BEZEICHNUNG
from XTRADE.KUNDE
where NUMMER > 99 and NUMMER not in (194, 196, 198)
and (DATUM_SCHLIESSUNG > GETDATE() or DATUM_SCHLIESSUNG is null)
order by BEZEICHNUNG
OPEN C1
PRINT #Kst + ' ' + #Markt
FETCH NEXT
FROM C1 into #Kst, #Markt
while ##FETCH_STATUS = 0
BEGIN
SET #STRSQL = #STRSQL + 'null as [' + #Markt + '], '
FETCH NEXT
FROM C1 into #Kst, #Markt
END
CLOSE C1
DEALLOCATE C1;
SET #STRSQL = left(#STRSQL, len(#Strsql) - 1) + ')'
DECLARE #Statement nvarchar(max) = ', b as (select 1 as Col1, 1 as Col2, 5 as Col3 union all select 2,2,12 union all select 3, 3, 42)';
DECLARE #Exec nvarchar(max) = #STRSQL + #Statement + 'select * from b cross join a';
print #Exec;
exec sp_executesql #Exec
I need to dynamically loop through column names and fetch the records.
Below is my table, and I am using SQL Server 2017.
-- DECLARE #cnt INT = 1;
-- WHILE #cnt < 3
-- BEGIN
-- SELECT 'Name' + #cnt FROM Emp
-- SET #cnt = #cnt + 1;
-- END;
--GO
but this query is not working.
How to loop through columns such as Name+#cnt?
Expected output:
User1
User2
User3
Emp table:
DECLARE #statement NVARCHAR(MAX) = '', #cnt INT = 1;
WHILE #cnt < 4
BEGIN
SET #statement = #statement + ' select Name' + CAST(#cnt AS VARCHAR) + ' from Emp;'
SET #cnt = #cnt + 1;
END
EXECUTE sp_executesql #statement
Dynamic SQL would be best fit for this task:
declare #sql varchar(1000) = '';
select #sql = #sql + ' select ' + name + ' from MYDATABASE.dbo.Emp ' from MYDATABASE.sys.columns
where name like 'Name[1-3]'
exec(#sql)
The query will search for column names like Name1, Name2, Name3 and construct three SELECT queries, which then will be executed by EXEC function.
NOTE: I assumed that table is in dbo schema and you need to use your database instead of MYDATABASE.
I am working with MS SQL. I wrote a simple code which goes through columns in my table and find the columns with more than 30% zero value. I will save name of column with more than 30% zero in #array.#count just has number of columns and #column has name of all columns.
DECLARE #array varchar(MAX)
DECLARE #sql varchar(MAX),
#column as varchar(MAX)
SELECT #count = (SELECT COUNT(*) FROM sys.columns WHERE object_id =
OBJECT_ID('UVE305_TREND_2.dbo.LOGTNK'))
WHILE #counT>0
BEGIN
SET #column = (SELECT name FROM sys.columns WHERE object_id = OBJECT_ID('UVE305_TREND_2.dbo.LOGTNK') AND column_id = #count)
SELECT #column
SET #array = (SELECT COUNT(#column) FROM UVE305_TREND_2.dbo.LOGTNK WHERE
#column = 0 )
select #array
SET #count= #count- 1
END;
IF #r_count >= CAST(#array AS INT)
SET #list= #column+','+#list;
when I tried to run my query I got following error:
Conversion failed when converting the varchar(max) value 'TNK_99' to
data type int.
'TNK_99' is my column name.
any help will be appreciated.
This expression:
WHERE #column = 0
Is being converted to:
WHERE 'TNK_99' = 0
That is, it is comparing a string (which happens to be a column name) to a number. By the rules of SQL the values are converted to numbers -- and you get a conversion error.
There is no simple way to solve this. The solution involves dynamic SQL, which is rather more complex than your code.
EDIT:
The code looks like:
DECLARE #sql NVARCHAR(MAX);
WHILE #count > 0
BEGIN
SET #column = (SELECT name FROM sys.columns WHERE object_id = OBJECT_ID('UVE305_TREND_2.dbo.LOGTNK') AND column_id = #count);
SET #SQL = 'SELECT #array = COUNT(*) FROM UVE305_TREND_2.dbo.LOGTNK WHERE ' + QUOTENAME(#column) + ' = 0 )'
EXEC sp_executesql #SQL, N'#array int output', #array=#array output;
SELECT #column, #array
SET #count= #count- 1
END;
As with the original code, this assumes that all columns are numeric. This code is a little vague on what "zero value" means. Perhaps you intend:
SET #SQL = 'SELECT #array = COUNT(*) FROM UVE305_TREND_2.dbo.LOGTNK WHERE ' + QUOTENAME(#column) + ' = ''0'' )'
I need some help with using a cursor and variable to populate a query. I am using SQL Server 2008 R2.
What I am trying to do is populate a temp table with inserts, run through the one column of data to generate a variable that will then populate a query that will check the number of rows in a table. Here is what I have so far:
IF OBJECT_ID('tempdb..#part_tables') IS NOT NULL DROP TABLE #part_tables
create table #Part_tables
(table_Name nvarchar(100))
Insert INTO #Part_Tables (table_name)
SELECT [InstancesTable] FROM [BAMPrimaryImport].[dbo].[bam_Metadata_Partitions]
WHERE [ArchivingInProgress]=0 and ArchivedTime IS NULL
and creationtime < dateadd(DD,-21,getdate())
GO
Insert INTO #Part_Tables (table_name)
SELECT [RelationshipsTable] FROM [BAMPrimaryImport].[dbo].[bam_Metadata_Partitions]
WHERE [ArchivingInProgress]=0 and ArchivedTime IS NULL
and creationtime < dateadd(DD,-21,getdate())
GO
DECLARE #count_query VARCHAR(MAX)
DECLARE #Value NVARCHAR(100)
SET #Value ='Select Table_Name from #Part_Tables'
SET #count_query ='
select count (*) from #Value with (NOLOCK)'
WHILE 1 = 1
BEGIN
EXEC(#count_query + ' option(maxdop 5) ')
IF ##rowcount < 1 BREAK;
END
If this will work, great! If you have a different / better way to do it, I would appreciate any guidance that someone could offer.
Here is a much simpler way to get the row counts from those tables. No need for cursors or while loops. And be careful with that NOLOCK hint...it can do a lot more than just dirty reads. http://blogs.sqlsentry.com/aaronbertrand/bad-habits-nolock-everywhere/
declare #SQL nvarchar(max) = ''
select #SQL = #SQL + 'select count(*) from ' + QUOTENAME(InstancesTable) + ' UNION ALL '
FROM [BAMPrimaryImport].[dbo].[bam_Metadata_Partitions]
WHERE [ArchivingInProgress] = 0
and ArchivedTime IS NULL
and creationtime < dateadd(Day, -21, getdate())
set #SQL = LEFT(#SQL, len(#SQL) - 10)
select #SQL --uncomment exec statement below when satisfied this is correct
--exec sp_executesql #SQL