Calling one sp from another - tsql

On this link
http://gallery.technet.microsoft.com/Compare-Data-0c5bfc87#content
we can find a stored procedure example which can compare two tables data. WHat I would like is to call this sp for each table in database. I have found next sp that will enumerate through all tables
http://weblogs.sqlteam.com/joew/archive/2007/10/23/60383.aspx
The problem is that I cannot get to pass properly parameters. Here is what I tried (I have placed both databases on local server):
Exec sp_MSforeachtable "EXEC sp_CompareTable dbName1, dbName2, NULL, PARSENAME('?', 1)"
and that fails with
Msg 102, Level 15, State 1, Line 1 Incorrect syntax near '[dbo].[Activities]'.
And the same message for every table. Can anyone help me with what I am doing wrong here?

I'll stick my neck out and post this as an answer because it's not formatted nicely as a comment.
Have you tried this:
sp_MSforeachtable "EXEC sp_CompareTable dbName1, dbName2, NULL, PARSENAME('[?]', 1)"
Update:
It looks like it doesn't like the PARSENAME. You could try this (I tried this on a version of sp_CompareTable with the EXEC changed to a PRINT).
Add this line to sp_CompareTable (before the EXEC):
SET #TableName = PARSENAME(#TableName,1)
Call it like this:
sp_MSforeachtable "EXEC sp_CompareTable dbName1, dbName2, dbo, '?'"
NB: This would be a quick fix for the case where you only have the "dbo" schema. It doesn't really answer the question of exactly why the original syntax doesn't work.
Update Again:
Here's a version of the Compare Table stored procedure which is tailored to run with sp_MSforeachtable
CREATE PROC [dbo].[uspCompareTable](#db1 varchar(250), #db2 sysname, #TableName sysname)
AS
declare #reason varchar(7)='Missing';
IF #TableName = '[dbo].[sysdiagrams]'
RETURN
IF CHARINDEX('.',#db1,1) <> 0
SET #db1=QUOTENAME(SUBSTRING(#db1,1, CHARINDEX('.',#db1,1)-1))+'.'+QUOTENAME(SUBSTRING(#db1, CHARINDEX('.',#db1,1)+1,DATALENGTH(#db1)-CHARINDEX('.',#db1,1)))
IF CHARINDEX('.',#db2,1) <> 0
SET #db2=QUOTENAME(SUBSTRING(#db2,1, CHARINDEX('.',#db2,1)-1))+'.'+QUOTENAME(SUBSTRING(#db2, CHARINDEX('.',#db2,1)+1,DATALENGTH(#db2)-CHARINDEX('.',#db2,1)))
EXEC ('
SELECT * FROM
(SELECT * FROM '+ #db1 + '.' + #TableName +'
EXCEPT
SELECT * FROM '+ #db2 + '.' + #TableName +') T
CROSS JOIN (SELECT '''+#reason +' in '+#db2 +'.' + #TableName+''' Reason) T2
UNION ALL
SELECT * FROM
(SELECT * FROM '+ #db2 + '.' + #TableName +'
EXCEPT
SELECT * FROM '+ #db1 + '.' + #TableName +' ) T
CROSS JOIN (SELECT ''' + #reason + ' in ' + #db1 + '.' + #TableName + ''' Reason) T2')
Here I'm assuming that schema will be part of the TableName (which it should be if you're calling from sp_MSforeachtable). Also a tweak to skip sysdiagrams which gets picked up on my system (SQL Server 2008 Express).
Usage would be
sp_MSforeachtable "EXEC uspCompareTable dbname1, dbname2, '?'"

Related

Loop through each table in database and get first three digits of one column

I am trying to create a table that will show me each table name, and the first three characters of the ID column (every table has this column), and then put that data into a table. We are using this to help map dependencies in our Salesforce Org which is replicated onto SQL using dbAMP. I adapted the code below as far as I could, and am looking for help to finish it.
UPDATE: The first half of the question is resolved, and the code now runs to give the first three characters of the ID. I still could use help in converting this code to spool the results into one single table.
USE Salesforce
GO
IF OBJECT_ID('tempdb..#tempResults') IS NOT NULL
DROP TABLE #tempResults
CREATE TABLE #tempResults
(
[Object_ID] VARCHAR(3)
--, [org] VARCHAR(100)
, [Table_Name] VARCHAR(100)
)
DECLARE cur CURSOR FOR
SELECT
'SELECT DISTINCT LEFT(' + QUOTENAME(c.COLUMN_NAME) + ',3) AS
[Object_ID], '''
--+ QUOTENAME(TABLE_CATALOG) + ' as [Org], '
+ QUOTENAME(TABLE_NAME) + ''' as [Table_Name] FROM '
+ QUOTENAME(TABLE_CATALOG) + '.' + QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME)
FROM INFORMATION_SCHEMA.COLUMNS AS c
WHERE
c.[DATA_TYPE] IN ('nchar','varchar', 'nvarchar')
AND c.[CHARACTER_MAXIMUM_LENGTH] = 18
and c.TABLE_NAME not like '%upload%'
and c.TABLE_NAME not like '%Delta%'
and c.TABLE_NAME not like '%Update%'
and c.TABLE_NAME not like '%Previous%'
and C.COLUMN_NAME = 'ID'
DECLARE #cmd VARCHAR(MAX);
OPEN cur;
FETCH NEXT FROM cur INTO #cmd;
WHILE ##FETCH_STATUS = 0
BEGIN
--PRINT #cmd
INSERT INTO #tempResults
EXEC(#cmd);
FETCH NEXT FROM cur INTO #cmd;
END
CLOSE cur;
DEALLOCATE cur;
SELECT * FROM #tempResults
I'm not getting the Column ID trimmed to the first 3 characters, and it's not outputting to a table. I'm not that familiar with cursors so I'd appreciate any help. Thanks,
You're really close. The way you've got your select written it's actually trimming the column name and then appending that 3-character column name to the string, rather than getting the first three characters from the actual data within the ID column.
Try updating your select so the LEFT brackets the QUOTENAME(c.COLUMN_NAME) inside the string, like below. This small change made your script work on my salesforce installation.
SELECT
'SELECT DISTINCT LEFT(' + QUOTENAME(c.COLUMN_NAME) + ',3) AS '
+ QUOTENAME(TABLE_CATALOG + '.' + TABLE_SCHEMA + '.' + TABLE_NAME) + ' FROM '
+ QUOTENAME(TABLE_CATALOG) + '.' + QUOTENAME(TABLE_SCHEMA) + '.'
+ QUOTENAME(TABLE_NAME)
FROM INFORMATION_SCHEMA.COLUMNS AS c
WHERE
c.[DATA_TYPE] IN ('nchar','varchar', 'nvarchar')
AND c.[CHARACTER_MAXIMUM_LENGTH] = 18
and c.TABLE_NAME not like '%upload%'
and c.TABLE_NAME not like '%Delta%'
and c.TABLE_NAME not like '%UPdate%'
and c.TABLE_NAME not like '%Previous%'
and C.COLUMN_NAME = 'ID'
Update to answer part two:
First you'll need a destination table - I've used a temporary table, but this will work just fine as a permanent table.
I've adjusted the code to output two columns instead of one: IDSubstring (your 3-character ID portion) and SourceTable (this is exactly the same information that you were using as a column name previously). This way we know which table the ID portion belongs to.
Then, inside the cursor, instead of just executing, we do this:
INSERT INTO #tempResults
EXEC(#cmd);
This will populate our table and give us selectable values.
CREATE TABLE #tempResults
(
[IDSubstring] VARCHAR(3)
, [SourceTable] VARCHAR(50)
)
DECLARE cur CURSOR FOR
SELECT
'SELECT DISTINCT LEFT(' + QUOTENAME(c.COLUMN_NAME) + ',3) AS [IDSubstring], '''
+ QUOTENAME(TABLE_CATALOG + '.' + TABLE_SCHEMA + '.' + TABLE_NAME) + ''' as [SourceTable] FROM '
+ QUOTENAME(TABLE_CATALOG) + '.' + QUOTENAME(TABLE_SCHEMA) + '.'
+ QUOTENAME(TABLE_NAME)
FROM INFORMATION_SCHEMA.COLUMNS AS c
WHERE
c.[DATA_TYPE] IN ('nchar','varchar', 'nvarchar')
AND c.[CHARACTER_MAXIMUM_LENGTH] = 18
and c.TABLE_NAME not like '%upload%'
and c.TABLE_NAME not like '%Delta%'
and c.TABLE_NAME not like '%UPdate%'
and c.TABLE_NAME not like '%Previous%'
and C.COLUMN_NAME = 'ID'
DECLARE #cmd VARCHAR(MAX);
OPEN cur;
FETCH NEXT FROM cur INTO #cmd;
WHILE ##FETCH_STATUS = 0
BEGIN
--PRINT #cmd
INSERT INTO #tempResults
EXEC(#cmd);
FETCH NEXT FROM cur INTO #cmd;
END
CLOSE cur;
DEALLOCATE cur;
SELECT * FROM #tempResults
Update 2
You're correct in assuming it's an issue with the '''
The ''' is there because we need to build our string with single quotes inside of it.
As examples:
SELECT '' returns nothing
while SELECT '''' returns '
And SELECT 'This is an example''' will return This is an example'
So the ''' are part of a larger string definition started with the initial ' before "This" and can be broken down this way - the first two single quotes are the single quote we want to print within the string and the third single quote is the string terminating quote. If you just run the select statement and look at what it outputs, you can see where each single quote has been inserted into the string.
Updated SELECT is below.
SELECT
'SELECT DISTINCT LEFT(' + QUOTENAME(c.COLUMN_NAME) + ',3) AS [Object_ID], '''
+ QUOTENAME(TABLE_CATALOG) + ''' as [Org], '''
+ QUOTENAME(TABLE_NAME) + ''' as [Table_Name] FROM '
+ QUOTENAME(TABLE_CATALOG) + '.' + QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME)
FROM INFORMATION_SCHEMA.COLUMNS AS c
WHERE
c.[DATA_TYPE] IN ('nchar','varchar', 'nvarchar')
AND c.[CHARACTER_MAXIMUM_LENGTH] = 18
and c.TABLE_NAME not like '%upload%'
and c.TABLE_NAME not like '%Delta%'
and c.TABLE_NAME not like '%Update%'
and c.TABLE_NAME not like '%Previous%'
and C.COLUMN_NAME = 'ID'

Dynamic SQL Query to Drop table

Looking for way to create some dynamic SQL to drop a table if it exists. However I can not seem to get the syntax correct. Here is the query so far (renamed fields for security)
DECLARE #TableNameNew NVARCHAR(MAX)
DECLARE #DynamicSQL2 NVARCHAR(MAX)
SET #TableNameNew = (SELECT 'tbl1_' + REPLACE(StaffCode,'.','') AS TableName
FROM tblEmployee WHERE (PCLogin = REPLACE(SYSTEM_USER, 'DOMAIN\', '')))
SET #DynamicSQL2 = 'IF OBJECT_ID(' + '''' + #TableNameNew + '''' + +','+'''U''' + ') IS NOT NULL DROP TABLE ' + #TableNameNew
EXEC #DynamicSQL2
This returns an error:
Could not find stored procedure 'IF OBJECT_ID('tbl1_ghewitt','U') IS NOT NULL DROP TABLE tbl1_ghewitt'.
Try
EXECUTE sp_executesql #DynamicSQL2
instead of
EXEC #DynamicSQL2

How can i pass parameters to a Common Table Expression inside a sp?

I want to rewrite a functioning, but slow stored procedure using CTE (common table Expression)
I have a big stored procedure where i build the nececary SQL dynamicaly based on parameter used. Currently there are 27 parameters, i compose the SQL string that i execute at the end it looks like:
DECLARE #SqlWhereClause NVARCHAR(MAX)
SET #SqlWhereClause = ' WHERE ([InTimeStamp] BETWEEN ''' + CONVERT(VARCHAR(19), #fromDate, 120) + ''' AND ''' + CONVERT(VARCHAR(19), #toDate, 120) + ''')'
IF #showOnlyErrors = '1'
BEGIN
SET #SqlWhereClause += ' AND Status = ''Error'''
END
IF LEN(LTRIM(RTRIM(#docNo))) > 0
BEGIN
IF #matchExact = '1'
BEGIN
SET #SqlWhereClause += ' AND DocumentNumber = ''' + #docNo + ''''
END
ELSE
BEGIN
SET #SqlWhereClause += ' AND (contains([DocumentNumber],'''+ #docNo +'''))'
END
END
At the end, i add the pagination and transform it to the final formSQL:
IF CONVERT(int, LTRIM(RTRIM(#takeRows))) > 0
BEGIN
SET #SqlOrderByClause += ' OFFSET ' + #rowNumberToSkip +' ROWS FETCH NEXT '+ #takeRows +' ROWS ONLY '
Set #RowCount = ' Select #totalRecords = count(1) from dbo.Messages WITH (NOLOCK) ' + #SqlWhereClause
END
SET #SQL = #SqlSelect + #SqlFrom + #SqlWhereClause + #SqlOrderByClause + ' ; ' + #RowCount
PRINT #SQL
EXECUTE sp_executesql #SQL, #params, #totalRecords OUTPUT
Everithing is working like a charm. No problems. Only performance problems. To solve one of it, i would try to use CTE (common table extpression)
But this is not working:
With DataSQL AS
(#SqlSelect + #SqlFrom + #SqlWhereClause + #SqlOrderByClause),
- incorrect syntax near #SqlSelect - Expecting '(' or Select.
I also tried this one:
WITH DataSQL AS
( Select #SqlSelect From #SqlFromFast
Where #SqlWhereClause Order By #SqlOrderByClause),
here i get:
An expression of non-boolean type specified in a context where a condition is expected, near 'Order'
Any idea? Or is it not possible to use CTE with multiple variables? All i found until now are simply queries with one, maybe two variables.
You could try:
WITH DataSQL AS
(
SELECT #SqlSelect SqlSelect
, #SqlFromFast SqlFromFast
, etcetera
)
Make sure you give aliases, or it won't work. I doubt it will help your performance issues though. You could try to use a temp table, that's usually quicker. you could also try to use inner variables:
DECLARE #Select VARCHAR(MAX) = #SQLSelect
And use those instead, that may help the optimizer, though that depends on your data.

Subtleties of SQL Server Variables

I have the following SQL Server stored procedure:
CREATE PROCEDURE ispsDcOhcAgg #TmpTableName NVARCHAR(50), #ListItem NVARCHAR(50)
AS
IF EXISTS (SELECT name
FROM sys.tables
WHERE name = #TmpTableName)
DROP TABLE #TmpTableName; -- This will not work.
GO
This will clearly not work (see the comment in the above snippit). The only (and very ugly) way I have found to get around this problem is to do the following
CREATE PROCEDURE ispsDcOhcAgg #TmpTableName NVARCHAR(50), #ListItem NVARCHAR(50)
AS
DECLARE #SQL NVARCHAR(4000)
SET #SQL = N'IF EXISTS (SELECT name ' +
N'FROM sys.tables ' +
N'WHERE name = N' + N'''' + #TmpTableName + N''') ' +
N'DROP TABLE ' + #TmpTableName + N';'
EXEC sp_executesql #SQL;
GO
which truly stinks and for large stored procedures, it's horrendous!
Is there another way of doing this that I don't know about?
Thanks for your time.
No, if you want to use a table name dynamically like this, you need to use dynamic SQL.
So you should make sure you don't open yourself up to nasty SQL injection risks!
Try something like this:
SET #SQL = 'IF EXISTS (SELECT name ' +
N'FROM sys.tables ' +
N'WHERE name = #TableName) ' +
N'DROP TABLE ' + QUOTENAME(#TmpTableName) + ';'
EXEC sp_executesql #SQL, N'#TableName sysname', #TmpTableName;
No, if you want to determine the table to be dropped at runtime, there is no alternative to dynamic SQL.
There is a slightly less ugly way: you only use dynamic SQL for the command that needs to be dynamic (the DROP command):
DECLARE #SQL NVARCHAR(100)
IF EXISTS (SELECT name
FROM sys.tables
WHERE name = #TmpTableName)
BEGIN
SET #SQL = N'DROP TABLE ' + #TmpTableName + N';'
EXEC sp_executesql #SQL;
END

Export query to text file

What I am trying to do is export a Tsql query to a csv file. Simple enough, however I need to be able to specify which fields are wrapped in quotes "". I can get my query to export with all the feilds wrapped.
"SCHEN001","Joe Bloggs Inc","1","1","1","1","1","1","13","6","Mr John Smith"
What I would like to export is
"SCHEN001","Joe Bloggs Inc",1,1,1,1,1,1,13,6,"Mr John Smith"
Is this possible using Tsql?
Any ideas would be greatly appreciated
Take a look at the bcp.exe utility. It is ment for bulk operations and you can specify templates for exports etc.
A link that seems reasonable: http://www.simple-talk.com/sql/database-administration/creating-csv-files-using-bcp-and-stored-procedures/
Another approach is to use SQL Server Integration Services (if you have MS SQL Server Standard or Enterprise edition)
or, alternatively, you can copy grid results into Excel, and export CSV from there :-)
Try to use this script.
Set variable #TblName to the name of your table.
The script uses information_schema.columns
to get the datatypes for every column in selected table.
DECLARE #TblName varchar(128)
DECLARE #WhereClause varchar(255)
DECLARE #cmd varchar(7000)
SET #TblName = '<YOUR TABLENAME>' --TABLENAME
SET #cmd = ''
create table #tableDef (id int identity (1,1), ColType int, ColName varchar(128))
--Fetch table information
insert #tableDef (ColType, ColName)
select case when DATA_TYPE like '%char%' then 1
when DATA_TYPE like '%datetime%' then 2
else 0 end ,
COLUMN_NAME
from information_schema.columns
where TABLE_NAME = #TblName
order by ORDINAL_POSITION
SELECT #cmd = #cmd
+ ' CASE WHEN ' + ColName + ' IS NULL '
+ ' THEN ''NULL'' '
+ ' ELSE '
+ case ColType
when 1 then ''''''''' + ' + ColName + ' + '''''''''
when 2 then ''''''''' + ' + 'CONVERT(VARCHAR(20),' + ColName + ')' + ' + '''''''''
else 'CONVERT(VARCHAR(20),' + ColName + ')' end
+ ' END + '','' + '
from #tableDef
order by id
select #cmd = 'SELECT ' + left(#cmd,len(#cmd)-8) + '+'''' FROM ' + #tblName
exec (#cmd)
drop table #tableDef