SQL Server 2014 Dynamic SQL - sql

Please take a look at the code and help me on the last part, --put all together = fail !!!
I wanted to generate a string for executing sp_executesql but I'm getting an error at the end. I also tried open cursor but it error out too.
Basically I'm trying to get actual row count per column per table for all tables
because general rowcount want give you if new column added and populated.
USE AdventureWorks2014
GO
-- Create view to hold the dataset
Alter view vColumnSchema
AS
with myColumnName
As
(
Select TOP (100) PERCENT
GetDate() As create_date,
##SERVERNAME As Server_Name,
DB_Name() AS database_name,
c.[object_id],
s.name AS [schema_name],
t.name as table_name,
c.name as column_name,
p.rows AS NUM_ROWS,
c.[precision]
from sys.tables as t
INNER JOIN sys.columns as c with(nolock) on t.[object_id] = c.[object_id]
INNER JOIN sys.indexes AS i ON t.object_id = i.object_id
INNER JOIN sys.partitions AS p ON i.object_id = p.object_id AND i.index_id = p.index_id
INNER JOIN sys.schemas AS s ON s.schema_id = t.schema_id
WHERE t.name NOT LIKE 'dt%'
AND i.object_id > 255
AND i.index_id <= 1
And s.name is not null
Order by s.name,
t.name
)
Select Top 100 percent
create_date,
Server_Name,
database_name,
[schema_name],
table_name,
column_name,
NUM_ROWS,
case when precision = 0 then
'Select Count('+column_name+')'+' from ' +[schema_name]+'.'+table_name+ ' where '+column_name+'<> '''' '
else
'Select Count(' +column_name+')'+' from ' +[schema_name]+ '.'+table_name+' where '+column_name+'> 0'
END as ColumnCount,
case when precision = 0
then ' <> '''' '
Else '> 0'
END As WhereClause
from myColumnName
Order by [schema_name],
table_name
GO
select * from vColumnSchema
where table_name = 'ProductCostHistory'
go
select * from Production.ProductCostHistory -- 395 records
--pick one record and test it = pass
Select Count(EndDate) from Production.ProductCostHistory where EndDate<> '' --200 records
GO
--test = pass on both whereClause
Declare #Column_name nvarchar(50) = 'EndDate'
Declare #Schema_Name nvarchar(20) = 'Production'
declare #table_name nvarchar(50) = 'ProductCostHistory'
declare #whereClause nvarchar(5) = '<> '''' '
declare #sqltext nvarchar(max) ='
select count('+#Column_name+') as ColumnCount from ' +#Schema_Name+'.'+#table_name+' where '+#Column_name+' '+#WhereClause+'
'
EXECUTE sp_executesql #sqltext
GO
--examine each column = pass
Declare #Column_name nvarchar(50) = 'select Column_name from vColumnSchema'
EXECUTE sp_executesql #Column_name
GO
Declare #schema_name nvarchar(50) = 'select schema_name from vColumnSchema'
EXECUTE sp_executesql #schema_name
GO
declare #table_name nvarchar(50) = 'select table_name from vColumnSchema'
EXECUTE sp_executesql #table_name
GO
declare #WhereClause nvarchar(50) = 'select WhereClause from vColumnSchema'
EXECUTE sp_executesql #WhereClause
GO
--put all together = fail !!!
declare #Column_name nvarchar(50) = 'select Column_name from vColumnSchema'
declare #schema_name nvarchar(50) = 'select schema_name from vColumnSchema'
declare #table_name nvarchar(50) = 'select table_name from vColumnSchema'
declare #WhereClause nvarchar(5) = 'select WhereClause from vColumnSchema';
declare #sqltext nvarchar(max) ='
select count('+#Column_name+') as ColumnCount from ' +#Schema_Name+'.'+#table_name+' where '+#Column_name+' '+#WhereClause+'
'
EXECUTE sp_executesql #sqltext
go

If you want the number of rows per column per table:
DECLARE #sql NVARCHAR(MAX) = '';
SELECT #sql = #sql +
N'SELECT '''
+ t.name + ''' AS TableName, '''
+ c.name + ''' AS ColName, COUNT(' + QUOTENAME(c.name) + ') AS [RowCount] FROM '
+ QUOTENAME(t.name) + ' UNION ALL' + CHAR(10)
FROM sys.tables t
INNER JOIN sys.columns c
ON c.object_id = t.object_id
WHERE
t.type = 'U'
AND c.system_type_id <> 35
SELECT #sql = LEFT(#sql, LEN(#sql) - 11)
PRINT #sql
EXECUTE sp_executesql #sql

Not Sure what your end game is But this might help and does not use dynamic sql ...
You can put the number of columns in your original query without a CTE
This puts the col count in the original select
Select TOP (100) PERCENT
GetDate() As create_date,
##SERVERNAME As Server_Name,
DB_Name() AS database_name,
c.[object_id],
s.name AS [schema_name],
t.name as table_name,
c.name as column_name,
p.rows AS NUM_ROWS,
c.[precision] ,
(Select COUNT(c2.object_id)
FROM sys.columns c2
INNER JOIN sys.tables t2 ON t2.object_id = c2.object_id
WHERE t2.object_id = t.object_id
GROUP BY c2.object_id) as ColCountThisTbl
from sys.tables as t
INNER JOIN sys.columns as c with(nolock) on t.[object_id] = c.[object_id]
INNER JOIN sys.indexes AS i ON t.object_id = i.object_id
INNER JOIN sys.partitions AS p ON i.object_id = p.object_id AND i.index_id = p.index_id
INNER JOIN sys.schemas AS s ON s.schema_id = t.schema_id
WHERE t.name NOT LIKE 'dt%'
AND i.object_id > 255
AND i.index_id <= 1
And s.name is not null
Order by s.name,
t.name `enter code here`

Related

How to drop default constraint based on column name

I was looking this article
and I have just a question how to add IFEXISTS() in the select part:
I want to run #command only if there some rows. I think it will throw an error if I dont check select part?
declare #table_name nvarchar(256)
declare #Command nvarchar(max) = ''
set #table_name = N'ATableName'
select #Command = #Command + 'ALTER TABLE ' + #table_name + ' drop constraint ' + d.name + CHAR(10)+ CHAR(13)
from sys.tables t
join sys.default_constraints d on d.parent_object_id = t.object_id
join sys.columns c on c.object_id = t.object_id
and c.column_id = d.parent_column_id
where t.name = #table_name and c.name in ('column1','column2','column3')
--print #Command
execute (#Command)

SQL Server how to find specific data across entire database

I have this script which finds all tables that contain data in column RGNCODE. I'm trying to amend this to find tables that contain a specific value for rgncode across all tables. So RGNCODE = 'UK'. Unsure where to add this in?
SELECT
sys.columns.name AS ColumnName,
tables.name AS TableName
FROM sys.columns
JOIN sys.tables ON sys.columns.object_id = tables.object_id
WHERE
sys.columns.name = 'rgncode' and
tables.name in (
SELECT [TableName] = so.name
FROM sysobjects so, sysindexes si
WHERE so.xtype = 'U' AND si.id = so.id
GROUP BY so.name
HAVING max(si.rows) > 0
)
A dynamic statement, using system catalog views, is an option here:
DECLARE #stm nvarchar(max) = N''
SELECT #stm = #stm +
CASE WHEN #stm = N'' THEN '' ELSE N'UNION ALL ' END +
N'SELECT ''' +
QUOTENAME(sch.[name]) + '.' + QUOTENAME(tab.[name]) + ''' AS TableName FROM ' +
QUOTENAME(sch.[name]) + '.' + QUOTENAME(tab.[name]) +
N' WHERE RGNCODE = ''UK'' HAVING COUNT(*) > 0 '
FROM sys.columns col
JOIN sys.tables tab ON col.object_id = tab.object_id
JOIN sys.schemas sch ON tab.schema_id = sch.schema_id
WHERE col.[name] = 'RGNCODE'
PRINT #stm
EXEC sp_executesql #stm
I think the following cursor will help you:
CREATE TABLE ##DataTable
(TblName VARCHAR(100),RowNum INT)
GO
DECLARE #TableName AS VARCHAR(200)
DECLARE #SQLText AS nVARCHAR(MAX)
DECLARE #TableCount AS INT
DECLARE TableDect CURSOR
FOR
SELECT
tables.name AS TableName
FROM
sys.columns
JOIN sys.tables ON
sys.columns.object_id = tables.object_id
WHERE
sys.columns.name = 'rgncode' and tables.name in (
SELECT
[TableName] = so.name
FROM
sysobjects so,
sysindexes si
WHERE
so.xtype = 'U'
AND
si.id = so.id
GROUP BY
so.name
HAVING max(si.rows) >0 )
OPEN TableDect
FETCH NEXT FROM TableDect INTO #TableName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQLText = N'SELECT #NumberOUT=COUNT(*) FROM ' + QUOTENAME(#TableName) + ' WHERE rgncode=''UK'''
SET #TableCount = 0
EXECUTE sp_executesql #SQLText ,N'#NumberOUT nvarchar(25) OUTPUT' ,#NumberOUT=#TableCount OUTPUT
INSERT INTO ##DataTable values(#TableName,#TableCount)
FETCH NEXT FROM TableDect INTO #TableName
END
CLOSE TableDect;
DEALLOCATE TableDect
SELECT * FROM ##DataTable

Query to find Size for each table By Filtering Rows

The Following Code will display the Size of each table in database
USE DatabaseName
GO
CREATE TABLE #temp (
table_name SYSNAME
, row_count INT
, reserved_size VARCHAR(50)
, data_size VARCHAR(50)
, index_size VARCHAR(50)
, unused_size VARCHAR(50)
)
SET NOCOUNT ON
INSERT #temp
EXEC sp_MSforeachtable 'sp_spaceused ''?'''
SELECT a.table_name
, a.row_count
, COUNT(*) AS col_count
, a.data_size
FROM #temp a
INNER JOIN INFORMATION_SCHEMA.columns b ON a.table_name COLLATE database_default
= b.table_name COLLATE database_default
GROUP BY a.table_name
, a.row_count
, a.data_size
ORDER BY CAST(REPLACE(a.data_size, ' KB', '') AS INTEGER) DESC
DROP TABLE #temp
Is there any way to Find the Space of All table by filtering the Row.
Like I have Foreign Key Company ID In All Table would it be possible if i want to know the space Occupied by Company
---Example ---
Purchase Table :
P_ID P_Description P_Price P_CompanyID
------------------------------------------
1 Mobile 100 1
2 Laptop 2100 1
3 Table 50 2
Sale table:
S_ID s_Description S_Price S_CompanyID
------------------------------------------
1 Mobile 110 1
2 Laptop 2200 1
3 Table 100 2
OutPut table:
Table Size Company
---------------------------------
Purchase 1.5MB 1
Sale 1.5MB 1
Purchase 1MB 2
Sale 1MB 2
I have Found the solution as per my Requirement. Thanks to Devart Help.
DECLARE #tblNAME NVARCHAR(50) = ''
DECLARE #colNAME NVARCHAR(50) = ''
DECLARE #SQL NVARCHAR(MAX) = ''
DECLARE CUR CURSOR FOR
SELECT NAME
FROM SYS.TABLES
WHERE TYPE = 'U'
AND SCHEMA_ID = 1
AND name NOT LIKE 'tt_%'
OPEN CUR
FETCH NEXT FROM CUR INTO #tblNAME
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #colNAME= c.name
FROM sys.columns c,sys.objects o
WHERE c.[object_id] = o.[object_id]
AND c.name like '%_CompanyID' And o.name=#tblNAME
if #colNAME is null or #colNAME =''
begin
FETCH NEXT FROM CUR INTO #tblNAME
end
Else
Begin
-- PRINT #tblNAME
--Print '---'+ #colNAME
SELECT #SQL += '
IF OBJECT_ID(''tt_' + o.name + ''') IS NOT NULL
DROP TABLE [tt_' + o.name + ']
SELECT *
INTO [tt_' + o.name + ']
FROM ' + QUOTENAME(SCHEMA_NAME(o.[schema_id])) + '.' + QUOTENAME(o.name) + '
WHERE '+#colNAME+' =10' -- your condition
FROM sys.objects o
WHERE o.[type] = 'U'
AND o.name NOT LIKE 'tt_%'
AND o.name LIKE #tblNAME
AND o.is_ms_shipped = 0
AND EXISTS(
SELECT *
FROM sys.columns c
WHERE c.[object_id] = o.[object_id]
AND c.name like '%_CompanyID' -- your column
)
Set #colNAME=null
FETCH NEXT FROM CUR INTO #tblNAME
end
END
CLOSE CUR
DEALLOCATE CUR
Print #SQL
EXEC sys.sp_executesql #SQL
SELECT REPLACE(o.name, 'tt_', '') as TableName, t.Size, t.Total_Rows, '1' as IsClient
FROM sys.objects o
JOIN (
SELECT
i.[object_id]
, size = SUM(a.total_pages) * 8. / 1024
, total_rows = SUM(CASE WHEN i.index_id IN (0, 1) AND a.[type] = 1 THEN p.[rows] END)
FROM sys.indexes i
JOIN sys.partitions p ON i.[object_id] = p.[object_id] AND i.index_id = p.index_id
JOIN sys.allocation_units a ON p.[partition_id] = a.container_id
GROUP BY i.[object_id]
) t ON o.[object_id] = t.[object_id]
WHERE o.name LIKE 'tt_%'
AND o.is_ms_shipped = 0
AND o.[type] = 'U'
ORDER BY t.size DESC
SET #SQL = ''
SELECT #SQL += '
DROP TABLE [' + o.name + ']'
FROM sys.objects o
WHERE o.[type] = 'U'
AND o.name LIKE 'tt_%'
AND o.is_ms_shipped = 0
EXEC sys.sp_executesql #SQL

Dynamic update statement with variable column names

We're looking to do an update in several SQL Server databases to change all NULL values in a certain table to be empty strings instead of NULL. We're potentially going to be doing this across hundreds of databases. The table name will always be the same, but the column names are variable based on how the front-end application is configured (don't judge... I didn't create this system).
Is there a way to do an update on all of these columns without knowing the column names ahead of time?
You can pass the name of the column in dynamic sql:
declare #sql nvarchar (1000);
set #sql = N'update table set ' + #column_name + '= ''''';
exec sp_executesql #sql;
You can look in the sys.columns table and join on the table name or object_id.
DECLARE #OBJ_ID INT
SELECT #OBJ_ID = OBJECT_ID
FROM SYS.tables
WHERE name = 'YOURTABLE'
SELECT * FROM SYS.columns
WHERE OBJECT_ID = #OBJ_ID
You could use the name field from the sys.columns query as a basis to perform the update on.
Assuming you want all columns of varchar/char types only (or change the type filter to whatever you need):
DECLARE #tableName varchar(10)
SET #tableName = 'yourtablenamehere'
DECLARE #sql VARCHAR(MAX)
SET #sql = ''
SELECT #sql = #sql + 'UPDATE ' + #tableName + ' SET ' + c.name + ' = '''' WHERE ' + c.name + ' IS NULL ;'
FROM sys.columns c
INNER JOIN sys.tables t ON c.object_id = t.object_id
INNER JOIN sys.types y ON c.system_type_id = y.system_type_id
WHERE t.name = #tableName AND y.name IN ('varchar', 'nvarchar', 'char', 'nchar')
EXEC (#sql)
This can be achieved with cursors. You first select the column names like #Darren mentioned, then you Set a Cursor with those values and loop:
Open oColumnsCursor
Fetch Next From oColumnscursor
Into #ColumnName
While ##FETCH_STATUS=0
Begin
Set #oQuery = 'Update [DB]..[Table] Set [' + #ColumnName + '] = ''NewValue'' Where [' + #ColumnName + '] = ''OldValue'''
Execute(#oQuery)
Fetch Next From oColumnscursor Into #ColumnName
Set #oCount = #oCount + 1
End
Close oColumnsCursor;
Deallocate oColumnsCursor;
This will work when you know the Table Name:
DECLARE #tableName varchar(10)
SET #tableName = 'Customers'
DECLARE #sql VARCHAR(MAX)
SET #sql = ''
SELECT #sql = #sql + 'UPDATE ' + #tableName + ' SET ' + c.name + ' = ISNULL('+ c.name +','''');'
FROM sys.columns c
INNER JOIN sys.tables t ON c.object_id = t.object_id
INNER JOIN sys.types y ON c.system_type_id = y.system_type_id
WHERE y.name IN ('varchar', 'nvarchar', 'char', 'nchar')
AND t.name = #tableName;
EXEC(#sql);
And this will iterate all Tables and all Columns in a Db:
DECLARE #sql VARCHAR(MAX)
SET #sql = ''
SELECT #sql = #sql + 'UPDATE ' + t.name + ' SET ' + c.name + ' = ISNULL('+ c.name +','''');'
FROM sys.columns c
INNER JOIN sys.tables t ON c.object_id = t.object_id
INNER JOIN sys.types y ON c.system_type_id = y.system_type_id
WHERE y.name IN ('varchar', 'nvarchar', 'char', 'nchar');
EXEC(#sql);
Below is the procedure.
ALTER PROCEDURE [dbo].[util_db_updateRow]
#colval_name NVARCHAR (30), -- column and values e.g. tax='5.50'
#idf_name NVARCHAR (300), -- column name
#idn_name NVARCHAR (300), -- column value
#tbl_name NVARCHAR (100) -- table name
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sql NVARCHAR(MAX)
-- construct SQL
SET #sql = 'UPDATE ' + #tbl_name + ' SET ' + #colval_name +
' WHERE ' + #idf_name + '=' + #idn_name;
-- execute the SQL
EXEC sp_executesql #sql
SET NOCOUNT OFF
RETURN
END
Below is the stored procedure where you can pass Schema Name, Table Name and list of column names separted by comma.It works only in Sql Server 2016 or higher.
CREATE OR ALTER PROCEDURE UpdateData
(#SchemaName NVARCHAR(Max),#TableName NVARCHAR(MAX),#ColumnNames NVARCHAR(MAX))
AS
BEGIN
DECLARE #DynamicSql NVARCHAR(MAX);
SET #DynamicSql = 'UPDATE ' +'[' +#SchemaName+'].' + '[' +#TableName+']' +' SET ' + STUFF((SELECT ', [' + C.name + '] = ' + '''NEW_VALUE'''
FROM sys.columns C
INNER JOIN sys.tables T ON T.object_id = C.object_id
INNER JOIN sys.schemas S ON T.schema_id = S.schema_id
WHERE
T.name = #TableName
AND S.Name = #SchemaName
AND [C].[name] in (SELECT VALUE FROM string_split(#ColumnNames,','))
FOR XML PATH('')), 1,1, '')
print #DynamicSql;
EXEC (#DynamicSql);
END

How to count empty tables in database?

Is there any way to count tables with no rows in my database with using T-SQL statement?
There you go... using a derived table.
SELECT * FROM
(
SELECT
[TableName] = so.name,
[RowCount] = MAX(si.rows)
FROM
sysobjects so,
sysindexes si
WHERE
so.xtype = 'U'
AND
si.id = OBJECT_ID(so.name)
GROUP BY
so.name
) sub
WHERE sub.[RowCount] = 0
I use the following:
SELECT t.NAME AS TableName, sum(p.rows) as RowCounts
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
WHERE
i.name IS NULL AND i.index_id <= 1
GROUP BY
t.NAME, i.object_id, i.index_id, i.name
HAVING SUM(p.rows) = 0
from khtan # SQL Server Forums, this is used to drop all empty tables, maybe you could adapt it to output a count?
declare #name varchar(128), #sql nvarchar(2000), #i int
select #name = ''
while #name < (select max(name) from sysobjects where xtype = 'U')
begin
select #name = min(name) from sysobjects where xtype = 'U' and name > #name
select #sql = 'select #i = count(*) from [' + #name + ']'
exec sp_executesql #sql, N'#i int out', #i out
if #i = 0
begin
select #sql = 'drop table [' + #name + ']'
print #sql
-- unmask next to drop the table
-- exec (#sql)
end
end
I don't have SQLServer here but I could take a stab at it if you like.