confusion with quotes and double quotes in a query - sql

i have a query that works beautifully:
CREATE Procedure BCP_Text_File
(
#table varchar(100),
#FileName varchar(100)
)
AS
If exists(Select * from information_Schema.tables where table_name=#table)
Begin
Declare #str varchar(1000)
set #str='Exec Master..xp_Cmdshell ''bcp "Select * from '+db_name()+'..'+#table+'" queryout "'+#FileName+'" -c'''
Exec(#str)
end
else
Select 'The table '+#table+' does not exist in the database'
but i need to add this in there:
select column_name
from information_schema.columns
where table_name = #table
order by ordinal_position
so far i have:
alter Procedure BCP_Text_File
(
#table varchar(100),
#FileName varchar(100)
)
AS
If exists(Select * from information_Schema.tables where table_name=#table)
Begin
Declare #str varchar(1000)
set #str='Exec Master..xp_Cmdshell ''bcp "
select column_name
from information_schema.columns
where table_name = '+db_name()+'..'+#table+'
order by ordinal_position
Select * from '+db_name()+'..'+#table+'" queryout "'+#FileName+'" -c'''
Exec(#str)
end
else
Select 'The table '+#table+' does not exist in the database'
but i think i am missplacing the single quotes and/or double quotes. i am adding this select statement so that my result has the field names as the first row.
thanks so much for any help or guidance.

Perhaps this is what you want? This assumes that (a) none of your column names have commas in them, and (b) the output of each column, when implicitly converted to a string, is okay.
ALTER PROCEDURE dbo.BCP_Text_File
#table NVARCHAR(255),
#filename VARCHAR(100)
AS
BEGIN
SET NOCOUNT ON;
IF OBJECT_ID(#table) IS NOT NULL
BEGIN
DECLARE
#sql NVARCHAR(MAX),
#cols NVARCHAR(MAX) = N'';
SELECT #cols += ',' + name
FROM sys.columns
WHERE [object_id] = OBJECT_ID(#table)
ORDER BY column_id;
SELECT #cols = STUFF(#cols, 1, 1, '');
SET #sql = N'EXEC master..xp_cmdshell ''bcp "SELECT '''''
+ REPLACE(#cols, ',', ''''',''''') + ''''' UNION ALL SELECT '
+ 'RTRIM(' + REPLACE(#cols, ',', '),RTRIM(') + ') FROM '
+ DB_NAME() + '..' + #table + '" queryout "' + #filename + '" -c''';
EXEC sp_executesql #sql;
END
ELSE
BEGIN
SELECT 'The table '+#table+' does not exist in the database';
END
END
GO
But I have to agree with the advice you've gotten from others on this and other questions - this approach is very brittle. You're trying to crack open a pistachio with a steamroller.
PS I removed references to INFORMATION_SCHEMA, because I think the catalog views are more reliable and more consistent.

Related

How to pass table name and column name dynamic in SQL

I was trying to pass table name and column name dynamic, this is as part of SSIS process I am trying this stored procedure below.
CREATE PROCEDURE [lnd].[Get_ANCNotullColumn]
(#PassedTableName AS NVarchar(255),
#PassedColumnName AS NVARCHAR(100))
AS
BEGIN
DECLARE #ActualTableName AS NVarchar(255)
SELECT #ActualTableName = QUOTENAME( TABLE_NAME )
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = #PassedTableName
DECLARE #sql AS NVARCHAR(MAX)
SELECT #sql = 'SELECT COUNT(*) FROM ' + #ActualTableName + ';'
DECLARE #final AS NVARCHAR(MAX)
SELECT #final = #sql + 'WHERE ' + #PassedColumnName + ' IS NULL OR ' + #PassedColumnName + '='''
EXEC(#SQL)
END
On executing this, I am NOT getting count as result, instead I am getting execution success.
EXEC [lnd].[Get_ANCNotullColumn] 'lnd.ANC_LND_ItemOverride', 'comments'
I need to get the count as output.
Also my simple direct query is like this
SELECT COUNT(*)
FROM lnd.ANC_LND_ItemOverride
WHERE Comments IS NULL OR Comments = '' -- 3 is the output
I think you may need to modify you value passing and your concatenation values.
from this statement you need to remove the semi colon as it will throw error
SELECT #sql = 'SELECT COUNT(*) FROM ' + #ActualTableName + ';'
While passing blank values you need additional quotes
SELECT #final = #sql + 'WHERE ' + #PassedColumnName + ' IS NULL OR ' + #PassedColumnName + '= '''''
While execution I believe you wanted to execute final instead of SQL
I think below should give your output:
CREATE PROC [lnd].[Get_ANCNotullColumn]( #PassedTableName as NVarchar(255),#PassedColumnName AS
NVARCHAR(100))
AS
BEGIN
DECLARE #ActualTableName AS NVarchar(255)
SELECT #ActualTableName = QUOTENAME( TABLE_NAME )
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = #PassedTableName
DECLARE #sql AS NVARCHAR(MAX)
SELECT #sql = 'SELECT COUNT(*) FROM ' + #ActualTableName + ' '
DECLARE #final AS NVARCHAR(MAX)
SELECT #final = #sql + 'WHERE ' + #PassedColumnName + ' IS NULL OR ' + #PassedColumnName + '='''''
EXEC(#final)
END

Create table as value from select statement

How to create table from select statement?
For example, I have format table as below:
FormatID Label
1 ID
2 Name
3 DOB
So I want to create new table with column name ID, Name, DOB.
Any pointer would be appreciated.
You could try like this:
-- build the SQL query
declare #sql nvarchar(max) = ''
select #sql = #sql + '[' + Label + '] nvarchar(255), ' from Format order by FormatID
select #sql = 'create table [MyTable] (' + #sql + ')'
-- create the table
EXECUTE sp_executesql #sql
go
-- lets see if the table actually got created
sp_help MyTable
go
you can do ...
declare #sql as varchar( max) ='';
declare #cln as varchar( max) ='';
select #cln =( SELECT Label + ' nvarchar(50) , ' from format FOR XML PATH('')
);
set #sql = 'create table tablename ( '+ #cln + ' );
sp_executesql #sql ;
#sandeep rawat. Thanks, I modified some and i can now got it ;)
declare #sql as nvarchar(max) ='';
declare #cln as nvarchar(max) ='';
select #cln =(SELECT REPLACE(label, ' ', '') + ' nvarchar(50),' from format FOR XML PATH('') );
set #cln = substring(#cln,1,len(#cln)-1)
set #sql = 'create table new_table ('+ #cln + ')';
print #sql
print len(#sql)
exec sp_executesql #sql ;
This will help you
select FormatID,Label into New_table_Name from Table_Name where 1=0;
There was little misunderstanding.
Hope it will help you.
create table #Tbl_Format
(FormatId int identity(1,1),
Lebel Varchar(64)
)
insert into #Tbl_Format
values('Id'),('Name'),('DOB')
Declare #Query nvarchar(512)
SET #Query= (SELECT ', ' + Lebel+' NVARCHAR(64)'
FROM #Tbl_Format
FOR XML PATH(''))
SET #Query='CREATE TABLE Table_Name ('+Substring(#Query,2,LEN(#Query))+')'
EXEC (#Query)
Thanks

SQL query to dynamically COUNT(FIELD) for all fields of table X

This should be such an easy thing, but it has me totally stumped.
You can easily return the count of each field of a table manually, with oneliners such as:
select count(FIELD1) from TABLE1 --42,706
select count(FIELD5) from TABLE1 --42,686
select count(FIELD9) from TABLE1 --2,918
This is slow and painful if you want to review several dozen tables the same way, and requires you to know the names of the fields in advance.
How handy would it be to have a script you can connect to any database, simply feed it a table name, and it will automatically return the counts for each field of that table?
Seems you can get half the work done with:
select COLUMN_NAME
from INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = 'TABLE1'
Something is flawed even with my barebones approach (explicitly hitting one field instead of them all):
declare #TABLENAME varchar(30), #FIELDNAME varchar(30)
set #TABLENAME = 'TABLE1'
set #FIELDNAME = (select top 1 COLUMN_NAME
from INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = #TABLENAME
and COLUMN_NAME = 'FIELD9')
select #FIELDNAME, count(#FIELDNAME) from TABLE1
The result is 42,706. Recall from my example above that FIELD9 only contains 2,918 values.
Even if that wasn't a problem, the more dynamic query would replace the last line with:
select #FIELDNAME, count(#FIELDNAME) from #TABLENAME
But SQL Server returns:
Must declare the table variable "#TABLENAME".
So I can avoid that by restructuring the query with a temp table:
declare #FIELDNAME varchar(30)
set #FIELDNAME = (select top 1 COLUMN_NAME
from INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = 'TABLE1'
and COLUMN_NAME = 'FIELD9')
if OBJECT_ID('TEMPDB..#TEMP1') is not null
drop table #TEMP1
select *
into #TEMP1
from TABLE1 --still not exactly dynamic!
select #FIELDNAME, count(#FIELDNAME) from #TEMP1
But that still brings us back to the original problem of returning 42,706 instead of 2,918.
I am running SQL Server 2008 R2, if it makes any difference.
Your query:
SELECT #FIELDNAME, COUNT(#FIELDNAME) FROM TABLE1
does not count FIELD9, #FIELDNAME is treated as a constant. It's like doing a COUNT(*).
You should use dynamic sql:
DECLARE #sql VARCHAR(MAX)
SET #sql = 'SELECT ''' + #fieldName + ''', COUNT([' + #fieldName + ']) FROM [' + #tableName + ']'
EXEC(#sql)
To get all columns and return it in a single result set without using a Temporary Table and CURSOR:
DECLARE #sql NVARCHAR(MAX) = ''
SELECT #sql = #sql +
'SELECT ''' + COLUMN_NAME + ''' AS ColName, COUNT([' + COLUMN_NAME + ']) FROM [' + #tableName + ']' + CHAR(10) +
'UNION ALL' + CHAR(10)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #tableName
SELECT #sql = LEFT(#sql, LEN(#sql) - 10)
EXEC(#sql)
Just set the #TargetTableName will do the job
DECLARE #TargetTableName sysname = '*'
SET NOCOUNT ON
DECLARE #TableName sysname, #ColumnName sysname, #Sql nvarchar(max)
DECLARE #TableAndColumn table
(
TableName sysname,
ColumnName sysname
)
DECLARE #Result table
(
TableName sysname,
ColumnName sysname,
NonNullRecords int
)
INSERT #TableAndColumn
SELECT o.name, c.name FROM sys.objects o INNER JOIN sys.columns c ON o.object_id = c.object_id
WHERE (o.name = #TargetTableName OR #TargetTableName = '*') AND o.type = 'U' AND c.system_type_id NOT IN (34, 35, 99) -- 34:image 35:text 99:ntext
ORDER BY c.column_id
DECLARE column_cursor CURSOR FOR SELECT TableName, ColumnName FROM #TableAndColumn
OPEN column_cursor
FETCH NEXT FROM column_cursor
INTO #TableName, #ColumnName
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #Sql = 'SELECT ''' + #TableName + ''' AS TableName, ''' + #ColumnName + ''' AS ColumnName, COUNT([' + #ColumnName + ']) AS NonNullRecords FROM [' + #TableName + ']'
print #Sql
INSERT #Result
EXEC (#Sql)
FETCH NEXT FROM column_cursor
INTO #TableName, #ColumnName
END
CLOSE column_cursor;
DEALLOCATE column_cursor;
SET NOCOUNT OFF
SELECT * FROM #Result

query every single database

I would like to search every database in my DB search (they all have the same table name) with the following:
SELECT DISTINCT NAME FROM TABLE WHERE NAME = 'blah'
I have tried this:
SET NOCOUNT ON;
IF OBJECT_ID (N'tempdb.dbo.#temp') IS NOT NULL
DROP TABLE #temp
CREATE TABLE #temp
(
[COUNT] INT
, DB VARCHAR(50)
)
DECLARE #TableName NVARCHAR(50)
SELECT #TableName = '[dbo].[TABLE]'
DECLARE #SQL NVARCHAR(MAX)
SELECT #SQL = STUFF((
SELECT CHAR(13) + 'SELECT ''' + name + ''', COUNT(1) FROM [' + name + '].' + #TableName
FROM sys.databases
WHERE OBJECT_ID(name + '.' + #TableName) IS NOT NULL
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
INSERT INTO #temp (DB, [COUNT])
EXEC sys.sp_executesql #SQL
SELECT *
FROM #temp t
The easiest way is to use (the undocumented) sp_msforeachdb. It's usage is:
exec sp_msforeachdb #command1 = N'insert into #tmp select "?", * from [?].dbo.table';
Of note is the ?, which is a placeholder for the database name. Within the #command1 string, double-quotes are used in place of single-quotes.
This procedure will also enumerate the system databases, and you can fairly easily sidestep that by appending if db_id("?") > 4 to skip them.

sp_MSforeachdb: only include results from databases with results

I'm running the below stored procedure sp_MSforeachdb with a simple command. My question is how to limit the result to show only the databases that have at least 1 record satisfying the command:
Here's my stored procedure:
EXECUTE master.sys.sp_MSforeachdb 'USE [?];
IF (EXISTS (SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = ''Tabs''))
BEGIN
SELECT ''?'' as dbname,T.TabName, T.TabPath
FROM Tabs T
WHERE T.TabID IN (
SELECT Distinct TM.TabID
FROM TabModules TM
WHERE mID IN (
...
)
)
ORDER BY T.TabName
END
'
Any ideas how I can modify the sp so that it doesn't display the databases that have empty results (see image)?
Well, first, stop using sp_MSforEachDb. Oh, the problems (if you want proof, see here).
How about:
DECLARE #cmd NVARCHAR(MAX) = N'', #sql NVARCHAR(MAX) = N'';
SELECT #cmd += N'IF EXISTS (SELECT 1 FROM '
+ QUOTENAME(name) + '.sys.tables WHERE name = N''Tabs'')
SET #sql += N''UNION ALL
SELECT ''''' + name + ''''',T.TabName
FROM ' + QUOTENAME(name) + '.dbo.Tabs AS T
WHERE EXISTS
(
SELECT 1 FROM ' + QUOTENAME(name) + '.dbo.TabModules AS TM
WHERE TM.TabID = T.TabID
AND TM.mID IN -- this should probably be exists too
(
...
)
)
'''
FROM sys.databases
WHERE state = 0 -- assume you only want online databases
AND database_id > 4; -- assume you don't want system dbs
EXEC sp_executesql #cmd, N'#sql NVARCHAR(MAX) OUTPUT', #sql OUTPUT;
SET #sql = STUFF(#sql, 1, 10, '') + N' ORDER BY TabName;';
PRINT #sql; -- this will appear truncated, but trust me, it is not truncated
-- EXEC sp_executesql #sql;
If you really want some unknown, arbitrary number of separate resultsets, the change is simple.
DECLARE #cmd NVARCHAR(MAX) = N'', #sql NVARCHAR(MAX) = N'';
SELECT #cmd += N'IF EXISTS (SELECT 1 FROM '
+ QUOTENAME(name) + '.sys.tables WHERE name = N''Tabs'')
SET #sql += N''SELECT ''''' + name + ''''',T.TabName
FROM ' + QUOTENAME(name) + '.dbo.Tabs AS T
WHERE EXISTS
(
SELECT 1 FROM ' + QUOTENAME(name) + '.dbo.TabModules AS TM
WHERE TM.TabID = T.TabID
AND TM.mID IN -- this should probably be exists too
(
...
)
)
ORDER BY T.TabName;
'';'
FROM sys.databases
WHERE state = 0 -- assume you only want online databases
--AND database_id > 4; -- assume you don't want system dbs
EXEC sp_executesql #cmd, N'#sql NVARCHAR(MAX) OUTPUT', #sql OUTPUT;
PRINT #sql; -- this will appear truncated, but trust me, it is not truncated
-- EXEC sp_executesql #sql;
You basically need another IF to only run the select if data exists.
Here's what i did to test.
On DB1:
create table mytesttable(a int)
insert mytesttable values(1)
On DB2:
create table mytesttable(a int)
So you want DB1 to return results, but DB2 not to. you can use the following sql:
EXECUTE master.sys.sp_MSforeachdb 'USE [?];
IF (EXISTS (SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = ''mytesttable''))
BEGIN
IF EXISTS (SELECT 1 FROM mytesttable) BEGIN
SELECT ''?'' as dbname,T.A
FROM mytesttable AS T
END
END
'
This only returns:
db1, 1