I have found a few similar questions to this but all the solutions seem to relate to passing date parameters to sp_executesql which is not what I am doing...
I have a .NET application which connects to a SQL database using the SqlConnection and SqlCommand classes. When I run the application and monitor the database queries using SQL Server Profiler, I can see that the .NET app is generating the following code:
exec sp_executesql N'IF object_id(''tempdb..#ParmsInProc'') IS NOT NULL DROP TABLE #ParmsInProc
SELECT o.name AS ProcName, o.object_id, p.name AS ParamName, p.system_type_id, p.user_type_id
INTO #ParmsInProc
FROM sys.objects o
JOIN sys.parameters p
ON o.object_id = p.object_id
JOIN sys.types t
ON t.system_type_id = p.system_type_id AND t.user_type_id = p.user_type_id
WHERE type_desc = ''SQL_STORED_PROCEDURE'' AND o.name = ''#sp''
SELECT *
FROM #ParmsInProc p
LEFT JOIN (
SELECT *
FROM #ParmsInProc
WHERE system_type_id = 56 AND user_type_id = 56
) ints
ON p.system_type_id = ints.system_type_id AND p.user_type_id = ints.user_type_id',N'#sp nvarchar(14)',#sp=N'sp_myProcedure'
If I run that code in SSMS, I get no results. However, if I copy out the query and replace the parameter placeholder with the actual value, I get
IF object_id('tempdb..#ParmsInProc') IS NOT NULL DROP TABLE #ParmsInProc
SELECT o.name AS ProcName, o.object_id, p.name AS ParamName, p.system_type_id, p.user_type_id
INTO #ParmsInProc
FROM sys.objects o
JOIN sys.parameters p
ON o.object_id = p.object_id
JOIN sys.types t
ON t.system_type_id = p.system_type_id AND t.user_type_id = p.user_type_id
WHERE type_desc = 'SQL_STORED_PROCEDURE' AND o.name = 'sp_myProcedure'
SELECT *
FROM #ParmsInProc p
LEFT JOIN (
SELECT *
FROM #ParmsInProc
WHERE system_type_id = 56 AND user_type_id = 56
) ints
ON p.system_type_id = ints.system_type_id AND p.user_type_id = ints.user_type_id
and if I run that, I get one result (which is the outcome I expect)
What could be causing this?
If you try to "debug" your initial query, you will see that #sp is being passed as a procedure name, instead of the actual value for your parameter.
You can see this by writing the following:
declare #spname nvarchar(100) = 'my_storedproc';
declare #sql nvarchar(max) = ' IF object_id(''tempdb..#ParmsInProc'') IS NOT NULL DROP TABLE #ParmsInProc
SELECT o.name AS ProcName, o.object_id, p.name AS ParamName, p.system_type_id, p.user_type_id
INTO #ParmsInProc
FROM sys.objects o
JOIN sys.parameters p
ON o.object_id = p.object_id
JOIN sys.types t
ON t.system_type_id = p.system_type_id AND t.user_type_id = p.user_type_id
WHERE type_desc = ''SQL_STORED_PROCEDURE'' AND o.name = ''#sp'';
SELECT *
FROM #ParmsInProc p
LEFT JOIN (
SELECT *
FROM #ParmsInProc
WHERE system_type_id = 56 AND user_type_id = 56
) ints
ON p.system_type_id = ints.system_type_id AND p.user_type_id = ints.user_type_id;'
print #sql
The following is being generated:
What you need to do is to just pass it as a parameter, no need for quotes.
I modified a little your query just to be able to print it, by storing the sql query into a variable but you can of course execute it as you had it, inline:
declare #spname nvarchar(100) = 'my_storedproc';
declare #sql nvarchar(max) = ' IF object_id(''tempdb..#ParmsInProc'') IS NOT NULL DROP TABLE #ParmsInProc
SELECT o.name AS ProcName, o.object_id, p.name AS ParamName, p.system_type_id, p.user_type_id
INTO #ParmsInProc
FROM sys.objects o
JOIN sys.parameters p
ON o.object_id = p.object_id
JOIN sys.types t
ON t.system_type_id = p.system_type_id AND t.user_type_id = p.user_type_id
WHERE type_desc = ''SQL_STORED_PROCEDURE'' AND o.name = #sp;
SELECT *
FROM #ParmsInProc p
LEFT JOIN (
SELECT *
FROM #ParmsInProc
WHERE system_type_id = 56 AND user_type_id = 56
) ints
ON p.system_type_id = ints.system_type_id AND p.user_type_id = ints.user_type_id;'
print #sql
exec sp_executesql #sql, N'#sp nvarchar(100)', #sp = #spname
This will execute successfully and you get to see the printed sql query under the Messages tab :)
Related
Can I drop all indexes of a schema in SQL Server according a condition?
I explain: I want drop all the indexes that contains a columns there types float, in schema XX.
I did this script below but I'm not sure of the line of the type of columns:
If Exists (Select *
From sys.indexes
Where name = 'indexName'
And Object_Id = Object_Id('schema xx.TABLE_NAME')
And Object_Id =Object_Id ('nchar', 'nvarchar'))
Drop Index indexNameOn dbo.Table_Name;
Thank you
This query, will help you to find all indexes where used columns with float datatype.
SELECT i.[name] as IndexName,
o.[name] as TableName
FROM sys.indexes i
INNER JOIN sys.objects o
ON i.[object_id] = o.[object_id] AND o.[type] = 'U' --USER_TABLE
INNER JOIN sys.index_columns ic
ON i.[object_id] = ic.[object_id]
INNER JOIN sys.columns c
ON c.column_id = ic.column_id AND c.[object_id] = ic.[object_id]
INNER JOIN sys.types t
ON c.system_type_id = t.system_type_id AND t.[name] = 'float'
On MSDN you can find all info about this tables. To DROP all automatically and search in all DB's you need to use dynamic SQL.
This query works in current DB only, we need to search in each not-system DB.
DECLARE #query nvarchar(max) = N''
-- Create a table to store a results of a search
IF OBJECT_ID(N'tempdb..##search_index') IS NOT NULL DROP TABLE ##search_index
CREATE TABLE ##search_index (
[db_name] sysname,
[index_name] sysname,
[schema_name] sysname,
[table_name] sysname
)
-- here we get all DBs except system (master, tempdb etc.)
;WITH cte AS (
SELECT CONCAT(N'USE ',QUOTENAME([name]),';') as db,
[name] as [db_name]
FROM sys.databases -- take all DBs
WHERE database_id > 4 -- not system
)
-- prepare a query for each DB
SELECT #query = #query + db + CHAR(13) +
N'INSERT INTO ##search_index
SELECT '''+ [db_name] + ''' as DB,
i.[name] as IndexName,
sch.[name] as SchemaName,
o.[name] as TableName
FROM sys.indexes i
INNER JOIN sys.objects o
ON i.[object_id] = o.[object_id] AND o.[type] = ''U'' -- USER_TABLE
INNER JOIN sys.schemas sch
ON o.[schema_id] = sch.[schema_id]
INNER JOIN sys.index_columns ic
ON i.[object_id] = ic.[object_id]
INNER JOIN sys.columns c
ON c.column_id = ic.column_id AND c.[object_id] = ic.[object_id]
INNER JOIN sys.types t
ON c.system_type_id = t.system_type_id AND t.[name] = ''float'';' + CHAR(13) + CHAR(13)
FROM cte
PRINT #query
-- execute query
EXEC(#query)
-- take a look at the results
SELECT *
FROM ##search_index
With the PRINT you can see what query was generated. The sample is here:
USE [Test];
INSERT INTO ##search_index
SELECT 'Test' as DB,
i.[name] as IndexName,
sch.[name] as SchemaName,
o.[name] as TableName
FROM sys.indexes i
INNER JOIN sys.objects o
ON i.[object_id] = o.[object_id] AND o.[type] = 'U' -- USER_TABLE
INNER JOIN sys.schemas sch
ON o.[schema_id] = sch.[schema_id]
INNER JOIN sys.index_columns ic
ON i.[object_id] = ic.[object_id]
INNER JOIN sys.columns c
ON c.column_id = ic.column_id AND c.[object_id] = ic.[object_id]
INNER JOIN sys.types t
ON c.system_type_id = t.system_type_id AND t.[name] = 'float';
USE [Another One];
INSERT INTO ##search_index
SELECT 'Another One' as DB,
...
The output is like:
db_name index_name schema_name table_name
Test PK_Period dbo Periods
Another Test PK_Test dbo Test
Then you need to check, if you want|need to delete this indexes. If you are sure about this you can use this:
DECLARE #query_to_drop nvarchar(max) = N''
SELECT #query_to_drop = #query_to_drop + N'USE ' + QUOTENAME([db_name]) +'; DROP INDEX ' + QUOTENAME([index_name]) + ' ON ' + QUOTENAME([schema_name]) +'.'+ QUOTENAME([table_name]) +';'+ CHAR(13)
FROM ##search_index
EXEC(#query_to_drop)
That will give you query like:
USE [Test]; DROP INDEX [PK_Period] ON [dbo].[Periods];
USE [Another Test]; DROP INDEX [PK_Test] ON [dbo].[Test];
I would like my query to return the table name, and rowcount for all of the tables on our two reporting servers. They both have the same tables. Also, I already added the linked server the other day between these two.
Query so far for one server, not sure how to add a third column connected with our other server though:
SELECT
t.NAME AS TableName,
p.[Rows]
FROM
sys.tables t
INNER JOIN
sys.indexes i ON t.OBJECT_ID = i.object_id
INNER JOIN
sys.partitions p ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id
INNER JOIN
sys.allocation_units a ON p.partition_id = a.container_id
WHERE
t.NAME NOT LIKE 'dt%' AND
i.OBJECT_ID > 255 AND
i.index_id <= 1
GROUP BY
t.NAME, i.object_id, i.index_id, i.name, p.[Rows]
ORDER BY
object_name(i.object_id)
Desired output:
TableName DB1_rows DB2_Rows
----------+-----------+-----------
Account | 20,000 | 19,388
Contacts | 1,234 | 1,390
Bla | 2,330 | 2,430
This would be a great use for Common Table Expressions (CTE's) as you can run multiple queries, then join those query results together and analyze/manipulate them in different ways:
/* Use the WITH keyword to start your first expression */
WITH SERVER_A AS (
SELECT
t.NAME AS TableName,
p.[Rows] AS NumRows
FROM
sys.tables t
INNER JOIN
sys.indexes i ON t.OBJECT_ID = i.object_id
INNER JOIN
sys.partitions p ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id
INNER JOIN
sys.allocation_units a ON p.partition_id = a.container_id
WHERE
t.NAME NOT LIKE 'dt%' AND
i.OBJECT_ID > 255 AND
i.index_id <= 1
GROUP BY
t.NAME, i.object_id, i.index_id, i.name, p.[Rows]
),
/* Then chain additional expressions (this time adding the linked server into the table name) */
SERVER_B AS (
SELECT
t.NAME AS TableName,
p.[Rows] AS NumRows
FROM
LINKED_SERVER_NAME.sys.tables t
INNER JOIN
LINKED_SERVER_NAME.sys.indexes i ON t.OBJECT_ID = i.object_id
INNER JOIN
LINKED_SERVER_NAME.sys.partitions p ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id
INNER JOIN
LINKED_SERVER_NAME.sys.allocation_units a ON p.partition_id = a.container_id
WHERE
t.NAME NOT LIKE 'dt%' AND
i.OBJECT_ID > 255 AND
i.index_id <= 1
GROUP BY
t.NAME, i.object_id, i.index_id, i.name, p.[Rows]
)
/* Then join the two together on a common column */
SELECT
A.TableName,
A.NumRows AS DB1_Rows,
B.NumRows AS DB2_Rows
FROM SERVER_A A
LEFT JOIN SERVER_B B ON
A.TableName = B.TableName
ORDER BY
A.TableName ASC
You could also accomplish this with APPLY statements or correlated sub-queries, but the advantage to using a CTE is that you're not running the sub-query for every single row that the parent query returns. Using a CTE you can run a query and then simply treat that query result as if it were a another table.
Obviously you'll want to test this. I don't have access to a SQL Server at the moment, so there may be a typo here or there.
DECLARE #RESULT TABLE (TableName VARCHAR(MAX), DB1_rows INT, DB2_Rows INT)
DECLARE #TABLENAME VARCHAR(MAX), #SQL VARCHAR(MAX)
DECLARE cCursor CURSOR FOR
SELECT name FROM sys.tables
OPEN cCursor
FETCH NEXT FROM cCursor INTO #TABLENAME
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQL = 'SELECT ''' + #TABLENAME + ''' , COUNT(*) FROM ' + #TABLENAME
DECLARE #FirstColumn VARCHAR(MAX) = (SELECT TOP 1 c.name FROM sys.columns c JOIN sys.tables t ON t.object_id = c.object_id WHERE t.name = #TABLENAME ORDER BY column_id)
SET #SQL = 'SELECT ''' + #TABLENAME + ''' , SUM(CASE WHEN A.' + #FirstColumn + ' IS NOT NULL THEN 1 ELSE 0 END), SUM(CASE WHEN B.' + #FirstColumn + ' IS NOT NULL THEN 1 ELSE 0 END) '
+'FROM LIVE.dbo.' + #TABLENAME + ' AS A FULL JOIN TEST.dbo.' + #TABLENAME + ' AS B on 1=0'
INSERT INTO #RESULT EXEC (#SQL)
FETCH NEXT FROM cCursor INTO #TABLENAME
END
CLOSE cCURSOR
DEALLOCATE cCURSOR
SELECT * FROM #RESULT
Just change the LIVE and TEST and the 'dbo' schema name on the second line of the 'SET #SQL' statement to the names of the 2 databases.
EDIT: Also you can add one of the database names.schema names to the 'SELECT name FROM sys.tables' statement at the top, plus any table name filtering you wanted to do.
If you are able to get the result from one server, then you can get the same from other server if the linked server is setup.
You can do it by using the 4 part name. For example:
Select ...
From ServerName.DBName.schema.TableName
...
I created a stored procedure that takes 3 arguments. I am having an issue with the 2nd argument ([HQ-211-STOCK-DBO]). I've been trying to figure it out for quite some time.
Here is the call to the SP:
EXEC [dbo].[spColumnsChanged] '[HQ-193-STOCK-DBO]','[HQ-211-STOCK-DBO]','arch_conv_col_diffs'
In the SP I have the following code:
CREATE PROCEDURE [dbo].[spColumnsChanged] (#sr_database nvarchar(128),#tr_database nvarchar(128),#diff_table nvarchar(128))
...
SET #DFCursor = CURSOR FAST_FORWARD FOR
SELECT o.name table_name, col.name column_name, t.name data_type, col.max_length, col.is_nullable
FROM #tr_database.sys.columns col
INNER JOIN #tr_database.dbo.sysobjects o on o.id = col.object_id
INNER JOIN #tr_database.sys.types t ON t.user_type_id = col.user_type_id
WHERE o.xtype = 'U' -- user tables
ORDER BY o.name, col.column_id
When I run the SP I receive the error:
"(Msg 102, Line 63) Incorrect syntax near '.'."
Line 63 is the FROM statement that uses the 3rd argument to the SP. (#tr_database)
The interesting thing is if I substitue the actual parameter value for the parameter like this:
SELECT o.name table_name, col.name column_name, t.name data_type, col.max_length, col.is_nullable
FROM [HQ-211-STOCK-DBO].sys.columns col
INNER JOIN [HQ-211-STOCK-DBO].dbo.sysobjects o on o.id = col.object_id
INNER JOIN [HQ-211-STOCK-DBO].sys.types t ON t.user_type_id = col.user_type_id
WHERE o.xtype = 'U' -- user tables
ORDER BY o.name, col.column_id
It works fine.
Can anyone spot the error?
What you need is dynamic sql.
You cannot set parameters that are the names of the tables / views / functions or other database objects. What you need is along these lines:
declare #sql nvarchar(max)
SET #SQL = 'SET #DFCursor = CURSOR FAST_FORWARD FOR
SELECT o.name table_name, col.name column_name, t.name data_type,
col.max_length, col.is_nullable
FROM '+#tr_database+'.sys.columns col
INNER JOIN '+#tr_databasee+'.dbo.sysobjects o
ON o.id = col.object_id
INNER JOIN '+#tr_databasee+'.sys.types t
ON t.user_type_id = col.user_type_id
WHERE o.xtype = ''U''
ORDER BY o.name, col.column_id'
EXECUTE(#SQL)
IMHO, mixing cursors with dynamic SQL will be nightmarish, both from a maintenance and performance perspective, but, for a once-off piece of code, it might work.
I have created a procedure which will output the INSERT script for a given table name. And in the procedure I am using this code:
DECLARE #owner VARCHAR(20) ,
#Database VARCHAR(50)
SELECT #owner = 'dbo' ,
#Database = 'Asmin'
SELECT sc.name
FROM sysobjects so
INNER JOIN syscolumns sc ON so.id = sc.id
INNER JOIN systypes st ON sc.xtype = st.xusertype
WHERE so.Name = 'Acisd'
But the problem I see is the procedure is in collection database and the table Acisd is in different database. So it's returning me nothing when I execute procedure.
How to fix this?
Not quite sure what you're going for here, but you could point this whole statement to the other database by fully qualifying the table references.
SELECT sc.name
FROM OtherDatabase.dbo.sysobjects so
INNER JOIN OtherDatabase.dbo.syscolumns sc ON so.id = sc.id
INNER JOIN OtherDatabase.dbo.systypes st ON sc.xtype = st.xusertype
WHERE so.Name = 'Acisd'
To do this inside a stored procedure you need to use dynamic SQL. For example:
declare #sql nvarchar(max);
set #sql = N'SELECT sc.name
FROM ' + quotename(#dbanme) + N'.sys.objects so
INNER JOIN ' + quotename(#dbname) + N'sys.columns sc ON so.id = sc.id
INNER JOIN ' + quotename(#dbname) + N'sys.types st ON sc.xtype = st.xusertype
WHERE so.Name = #objectName';
exec sp_executesql #sql, N'#objectName sysname', #objectName;
In sybase there is function db_name() to ktakeout current database ... not sure about sqlserver
In MySQL there is a "Use" clause, which can precede any select statement. I believe this is also the case in SQL - Server.
As previus said the most easy way to do it is to simply start by
USE [otherDatabasename]
GO
DECLARE #owner VARCHAR(20) ,
#Database VARCHAR(50)
SELECT #owner = 'dbo' ,
#Database = 'Asmin'
SELECT sc.name
FROM sysobjects so
INNER JOIN syscolumns sc ON so.id = sc.id
INNER JOIN systypes st ON sc.xtype = st.xusertype
WHERE so.Name = 'Acisd'
I can get a list of unique constraints fairly easily with the following query:
select * from INFORMATION_SCHEMA.TABLE_CONSTRAINTS where CONSTRAINT_TYPE='UNIQUE'
But how do I get a list of the columns that each unique constraint applies to?
Ed is correct, the columns are exposed on the constraint column usage view, here is the SQL for it.
select TC.Constraint_Name, CC.Column_Name from information_schema.table_constraints TC
inner join information_schema.constraint_column_usage CC on TC.Constraint_Name = CC.Constraint_Name
where TC.constraint_type = 'Unique'
order by TC.Constraint_Name
See INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE
SELECT *
FROM sys.indexes i
JOIN sys.index_columns ic
ON i.index_id = ic.index_id
AND i.object_id = ic.object_id
WHERE i.is_unique_constraint = 1;
Necromancing.
INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE is incredibly slow.
Use sys.indexes, join the table, schema, object, and as an added bonus, you get not only unique constraints, but also unique indices, including filter.
-- CREATE TABLE dbo.T_User( USR_ID int NOT NULL, USR_User nvarchar(256) NULL, USR_Status int NOT NULL );
-- ALTER TABLE dbo.T_User ADD CONSTRAINT UC_T_User_USR_User UNIQUE(USR_User)
-- IF NOT EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'dbo.T_User') AND name = N'IX_T_User_USR_User')
-- CREATE UNIQUE INDEX IX_T_User_USR_User ON dbo.T_User(USR_User)
-- WHERE (USR_User IS NOT NULL AND USR_Status=(1))
SELECT
sch.name
,st.name
,i.name
,i.has_filter
,i.filter_definition
,i.is_unique
,i.is_primary_key
,i.is_unique_constraint
,CASE WHEN i.is_unique_constraint = 1
THEN N'ALTER TABLE ' + QUOTENAME(sch.name) + N'.' + QUOTENAME(st.name) + N' DROP CONSTRAINT ' + QUOTENAME(i.name) + N'; '
ELSE N'DROP INDEX ' + QUOTENAME(i.name) + N' ON ' + QUOTENAME(sch.name) + '.' + QUOTENAME(st.name) + '; '
END AS sql
FROM sys.indexes AS i
INNER JOIN sys.index_columns AS ic
ON i.index_id = ic.index_id
AND i.object_id = ic.object_id
INNER JOIN sys.tables AS st
ON st.object_id = i.object_id
INNER JOIN sys.objects AS syso
ON syso.object_id = st.object_id
AND syso.is_ms_shipped = 0
INNER JOIN sys.schemas AS sch
ON sch.schema_id = st.schema_id
WHERE (1=1)
AND NOT EXISTS
(
SELECT *
FROM sys.extended_properties AS xp
WHERE xp.minor_id = 0
AND xp.major_id = st.object_id
AND xp.name = 'microsoft_database_tools_support'
)
-- AND sch.name = 'dbo'
-- AND st.name = 'T_Benutzer'
AND
(
i.is_unique_constraint = 1
OR
(i.is_unique = 1 AND i.is_primary_key = 0)
)
;
Just for reference of mySQL users,
Same thing can be achieved with below queries:
To find any unique constraint on a table
select * from INFORMATION_SCHEMA.TABLE_CONSTRAINTS where CONSTRAINT_TYPE='UNIQUE' and table_name='db_my_table'
To find unique column list with all column
select * from INFORMATION_SCHEMA.KEY_COLUMN_USAGE where CONSTRAINT_NAME='cons_name' and TABLE_NAME='db_my_table'
to find unique column list with required view
select CONSTRAINT_NAME,COLUMN_NAME,TABLE_NAME from INFORMATION_SCHEMA.KEY_COLUMN_USAGE where CONSTRAINT_NAME='cons_name' and TABLE_NAME='db_my_table'
Here is a better solution, which lists the constraint columns in a proper sort order, with added ASC/DESC flag. It also allows filtering by catalog, schema or table name.
SELECT sh.name AS schema_name,
i.name AS constraint_name,
t.name AS table_name,
c.name AS column_name,
ic.key_ordinal AS column_position,
ic.is_descending_key AS is_desc
FROM sys.indexes i
INNER JOIN sys.index_columns ic
ON i.index_id = ic.index_id AND i.object_id = ic.object_id
INNER JOIN sys.tables AS t
ON t.object_id = i.object_id
INNER JOIN sys.columns c
ON t.object_id = c.object_id AND ic.column_id = c.column_id
INNER JOIN sys.objects AS syso
ON syso.object_id = t.object_id AND syso.is_ms_shipped = 0
INNER JOIN sys.schemas AS sh
ON sh.schema_id = t.schema_id
INNER JOIN information_schema.schemata sch
ON sch.schema_name = sh.name
WHERE i.is_unique_constraint = 1
-- AND sch.catalog_name = 'EmployeesQX'
-- AND sh.name = 'dbo'
ORDER BY sh.name, i.name, ic.key_ordinal;