I am trying to create a SQL View that will load columns:
[item],[manufacturer_item],[symbol],[footprint]
from any table that matches the name ENG_Parameter_%.
I need to leave it generic because, at any point in time, ENG_Parameter_% might have new or removed tables, so I cannot just hard-code any table names.
Is there a SQL command that could be made to generate this?
The dynamic sql to create the views:
DECLARE #SQL nvarchar(2000);
SET #SQL=N' SELECT
''CREATE VIEW [vw'' + t.name + '']
AS
SELECT [item],[manufacturer_item],[symbol],[footprint]
FROM ['' + t.name + '']'' AS sql_for_view
FROM sys.columns c
INNER JOIN sys.tables t
ON c.object_id=t.object_id
WHERE c.name LIKE ''ENG_Parameter_%''';
exec sp_executesql #SQL;
Thanks to #OwlsSleeping answer (StackOverflow SQL-UNION ALL), this ended up with exactly what I needed:
DECLARE #SQL nvarchar(max)
select #SQL = COALESCE(#SQL , '') + 'SELECT [item],[manufacturer_item],[symbol],
[footprint] FROM [' + TABLE_NAME + '] UNION ALL '
FROM INFORMATION_SCHEMA.TABLES where TABLE_NAME LIKE 'ENG_Parameters_%'
SELECT #SQL = LEFT(#SQL, LEN(#SQL) - 11)
exec sp_executesql #SQL;
Related
I am trying to iterate over all the tables with a given schema name and make a copy in the same db with another given schema.
This is the script I am using:
use DoctorWho
declare #sql_query as nvarchar(max)
select #sql_query = concat('insert into doctor_generated.' , table_name , ' select * from ' , table_name , ';')
FROM INFORMATION_SCHEMA.tables
WHERE table_schema LIKE 'dbo%';
exec (#sql_query);
However this throws an error:
Invalid object name 'doctor_generated.tblEpisodeEnemy
Upon searching this error, I've refreshed the local cache & made sure I am using the correct db.
Is there anything I am missing?
I suspect what you actually want is something like this. Firstly use string aggregation for your dynamic statement; I assume you are on a fully supported version of SQL Server as you don't state you aren't. Next use QUOTENAME to properly quote your objects and avoid injection.
Then you can execute your dynamic statement:
DECLARE #SQL nvarchar(MAX),
#CRLF nchar(2) = NCHAR(13) + NCHAR(10);
SELECT #SQL = STRING_AGG(N'SELECT * INTO doctor_generated.' + QUOTENAME(t.name) + N' FROM ' + QUOTENAME(s.name) + N'.' + QUOTENAME(t.name) + N';',#CRLF)
FROM sys.schemas s
JOIN sys.tables t ON s.schema_id = t.schema_id
WHERE s.[name] = N'dbo';
--PRINT #SQL;
EXEC sys.sp_executesql #SQL;
I have a
Checklist table and
there is 27 columns named "check1", "check2"..."check27".I would like to get all this values doing a query something like:
SELECT "check*" FROM Checklist;
Is this possible?
Which database? postgres, sqlite, mysql?
If select * is not an option, the most flexible approach is creating a dynamic query. You will first need to get the column names and then build your query:
DECLARE #tableName as varchar(100);
SET #tableName = 'Checklist';
DECLARE #columnList varchar(300);
SELECT #columnList = COALESCE(#columnList + ', ', '') + sc.name
FROM sysobjects so
INNER JOIN syscolumns sc ON so.id = sc.id
WHERE so.name = #tableName
AND sc.name LIKE 'check%'
DECLARE #query as varchar(4000);
SET #query = 'SELECT ' + #columnList + ' FROM ' + #tableName;
EXEC(#query);
The ending #query should contain SELECT check1, check2, check... FROM Checklist.
In Sql Server, this is terrible but you could do it... Building dynamic SQL
check% being your check* in the Select #columns query
DECLARE #columns NVARCHAR(max);
SELECT #columns = STUFF((
SELECT ',' + column_name
FROM INFORMATION_SCHEMA.columns
WHERE table_name = 'Checklist'
AND column_name LIKE 'check%'
FOR XML path('')
), 1, 1, '')
DECLARE #statement nvarchar(max) = 'SELECT ' + #columns + ' FROM Checklist'
EXECUTE sp_executesql #statement
Troll ass answer...
SELECT check1,check2,check3,check4,check5,check6,check7,check8,check9,check10,check11,check12,check13,check14,check15,check16,check17,check18,check19,check20,check21,check22,check23,check24,check25,check26,check27 FROM Checklist;
I can successfully query the same table in multiple databases as follows:
DECLARE #command varchar(1000)
SELECT #command = 'select * from table'
EXEC sp_MSforeachdb #command
However, all of these results are, as expected, returned in different result windows. What's the easiest way to perform a union of all of these results?
Please stop using sp_MSforeachdb. For anything. Seriously. It's undocumented, unsupported, and spectacularly broken:
Making a more reliable and flexible sp_MSforeachdb
Execute a Command in the Context of Each Database in SQL Server
If you know that all databases have the same table (and that they all have the same structure!), you can do this:
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'';
SELECT #sql = #sql + N'UNION ALL SELECT col1,col2 /*, etc. */
FROM ' + QUOTENAME(name) + '.dbo.tablename'
FROM sys.databases WHERE database_id > 4 AND state = 0;
SET #sql = STUFF(#sql, 1, 10, '');
EXEC sp_executesql #sql;
This ignores system databases and doesn't attempt to access any databases that are currently not ONLINE.
Now, you may want to filter this further, e.g. not include any databases that don't have a table called tablename. You'll need to nest dynamic SQL in this case, e.g.:
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'DECLARE #cmd NVARCHAR(MAX);
SET #cmd = N'''';';
SELECT #sql = #sql + N'
SELECT #cmd = #cmd + N''UNION ALL
SELECT col1,col2 /*, etc. */ FROM '
+ QUOTENAME(name) + '.dbo.tablename ''
WHERE EXISTS (SELECT 1 FROM ' + QUOTENAME(name)
+ '.sys.tables AS t
INNER JOIN ' + QUOTENAME(name) + '.sys.schemas AS s
ON t.[schema_id] = s.[schema_id]
WHERE t.name = N''tablename''
AND s.name = N''dbo'');'
FROM sys.databases WHERE database_id > 4 AND state = 0;
SET #sql = #sql + N';
SET #cmd = STUFF(#cmd, 1, 10, '''');
PRINT #cmd;
--EXEC sp_executesql #cmd;';
PRINT #sql;
EXEC sp_executesql #sql;
This doesn't validate the column structure is compatible, but you'll find that out pretty quickly.
Another way to skin this cat is to use dynamic SQL:
DECLARE #sql varchar(max);
SELECT #sql = Coalesce(#sql + ' UNION ALL ', '') + 'SELECT list, of, columns FROM ' + QuoteName(name) + '.schema.table'
FROM sys.databases
;
PRINT #sql
--EXEC (#sql);
Had some collation issues and had to use
AND COLLATION_NAME = 'SQL_Latin1_General_CP1_CI_AS'
I just came up with an idea for a piece of code to show all the distinct values for each column, and count how many records for each. I want the code to loop through all columns.
Here's what I have so far... I'm new to SQL so bear with the noobness :)
Hard code:
select [Sales Manager], count(*)
from [BT].[dbo].[test]
group by [Sales Manager]
order by 2 desc
Attempt at dynamic SQL:
Declare #sql varchar(max),
#column as varchar(255)
set #column = '[Sales Manager]'
set #sql = 'select ' + #column + ',count(*) from [BT].[dbo].[test] group by ' + #column + 'order by 2 desc'
exec (#sql)
Both of these work fine. How can I make it loop through all columns? I don't mind if I have to hard code the column names and it works its way through subbing in each one for #column.
Does this make sense?
Thanks all!
You can use dynamic SQL and get all the column names for a table. Then build up the script:
Declare #sql varchar(max) = ''
declare #tablename as varchar(255) = 'test'
select #sql = #sql + 'select [' + c.name + '],count(*) as ''' + c.name + ''' from [' + t.name + '] group by [' + c.name + '] order by 2 desc; '
from sys.columns c
inner join sys.tables t on c.object_id = t.object_id
where t.name = #tablename
EXEC (#sql)
Change #tablename to the name of your table (without the database or schema name).
This is a bit of an XY answer, but if you don't mind hardcoding the column names, I suggest you do just that, and avoid dynamic SQL - and the loop - entirely. Dynamic SQL is generally considered the last resort, opens you up to security issues (SQL injection attacks) if not careful, and can often be slower if queries and execution plans cannot be cached.
If you have a ton of column names you can write a quick piece of code or mail merge in Word to do the substitution for you.
However, as far as how to get column names, assuming this is SQL Server, you can use the following query:
SELECT c.name
FROM sys.columns c
WHERE c.object_id = OBJECT_ID('dbo.test')
Therefore, you can build your dynamic SQL from this query:
SELECT 'select '
+ QUOTENAME(c.name)
+ ',count(*) from [BT].[dbo].[test] group by '
+ QUOTENAME(c.name)
+ 'order by 2 desc'
FROM sys.columns c
WHERE c.object_id = OBJECT_ID('dbo.test')
and loop using a cursor.
Or compile the whole thing together into one batch and execute. Here we use the FOR XML PATH('') trick:
DECLARE #sql VARCHAR(MAX) = (
SELECT ' select ' --note the extra space at the beginning
+ QUOTENAME(c.name)
+ ',count(*) from [BT].[dbo].[test] group by '
+ QUOTENAME(c.name)
+ 'order by 2 desc'
FROM sys.columns c
WHERE c.object_id = OBJECT_ID('dbo.test')
FOR XML PATH('')
)
EXEC(#sql)
Note I am using the built-in QUOTENAME function to escape column names that need escaping.
You want to know the distinct coulmn values in all the columns of the table ? Just replace the table name Employee with your table name in the following code:
declare #SQL nvarchar(max)
set #SQL = ''
;with cols as (
select Table_Schema, Table_Name, Column_Name, Row_Number() over(partition by Table_Schema, Table_Name
order by ORDINAL_POSITION) as RowNum
from INFORMATION_SCHEMA.COLUMNS
)
select #SQL = #SQL + case when RowNum = 1 then '' else ' union all ' end
+ ' select ''' + Column_Name + ''' as Column_Name, count(distinct ' + quotename (Column_Name) + ' ) As DistinctCountValue,
count( '+ quotename (Column_Name) + ') as CountValue FROM ' + quotename (Table_Schema) + '.' + quotename (Table_Name)
from cols
where Table_Name = 'Employee' --print #SQL
execute (#SQL)
I have some tables that I need to drop on a regular basis. The names of the tables sometimes change but the table names always begin with 'db_comp_temp'. Is it possible to write some SQL that will in effect do something like:
DROP TABLE db_comp_temp*
Thanks in advance,
No, there is no wildcard support in DDL.
DECLARE #sql nvarchar(max) = N'';
SELECT #sql += N'DROP TABLE ' + QUOTENAME(s.name)
+ '.' + QUOTENAME(t.name) + ';
' FROM sys.tables AS t
INNER JOIN sys.schemas AS s
ON t.[schema_id] = s.[schema_id]
WHERE t.name LIKE N'db[_]comp[_]temp%';
PRINT #sql;
-- EXEC sys.sp_executesql #sql;
Or:
DECLARE #sql nvarchar(max) = N'';
SELECT #sql += N'
,' + QUOTENAME(s.name) + '.' + QUOTENAME(t.name)
FROM sys.tables AS t
INNER JOIN sys.schemas AS s
ON t.[schema_id] = s.[schema_id]
WHERE t.name LIKE N'db[_]comp[_]temp%';
SET #sql = N'DROP TABLE ' + STUFF(#sql, 1, 1, '');
PRINT #sql;
-- EXEC sys.sp_executesql #sql;
You could also do it with FOR XML PATH but I don't think it's necessary when you're not grouping the result into another outer query.