patch datetime in varchar column - sql

i have about 12 tables i need to iterate thru and update all datetime fields (which is a varchar type) check if it's a date and if so, add 5hrs to account for utc adjustment or if it's an invalid date just set it to null. Below is a template I came up with. Just wondering if there are some other ways to do this?
update Tables_1
set releasedate = CASE ISDATE(releasedate)
WHEN 1 THEN DATEADD(HH, 5, releasedate)
ELSE NULL
END,
returndate = CASE ISDATE(returndate)
WHEN 1 THEN DATEADD(HH, 5, returndate)
ELSE NULL
END
given: sql server 2008, i know the columns and tables already that have the datetime type stored as varchar.
bonus request to add another check. If it is a date and the time is not specified, set the time to 5:00 AM

If they all end with date you could build it dynamically:
DECLARE #sql NVARCHAR(MAX) = N'';
SELECT #sql += ', ' + name = CASE ISDATE(' + name + ') WHEN 1 THEN
DATEADD(HOUR, 5, ' + name + ' ELSE NULL END'
FROM sys.columns
WHERE name LIKE '%date' AND [object_id] = OBJECT_ID('dbo.Tables_1');
SELECT #sql = 'UPDATE dbo.Tables_1 SET ' + STUFF(#sql, 1, 1, N'');
PRINT #sql;
-- EXEC sp_executesql #sql;
Now if you want to do that for 12 tables, you could just do this in a loop, e.g.
DECLARE #t SYSNAME, #sql NVARCHAR(MAX);
DECLARE c CURSOR LOCAL STATIC FORWARD_ONLY READ_ONLY
FOR
SELECT name FROM sys.tables
WHERE name IN ('Tables_1' --, other tables
);
OPEN c;
FETCH NEXT FROM c INTO #t;
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #sql = ', ' + name = CASE ISDATE(' + name + ') WHEN 1 THEN
DATEADD(HOUR, 5, ' + name + ' ELSE NULL END'
FROM sys.columns
WHERE name LIKE '%date' AND [object_id] = OBJECT_ID(#t);
SELECT #sql = 'UPDATE ' + #t + ' SET ' + STUFF(#sql, 1, 1, N'');
PRINT #sql;
-- EXEC sp_executesql #sql;
FETCH NEXT FROM c INTO #t;
END
CLOSE c;
DEALLOCATE c;

Related

Select all databases that have a certain table and a certain column

I need to create a query that is executed on all databases of my SQL server instance. An additional constraint is, that the query should only be executed on databases that contain a special table with a special column. Background is that in some databases the special table does (not) have the special column.
Based on this solution, what I have until now is a query that executes only on databases that contain a certain table.
SELECT *
FROM sys.databases
WHERE DATABASEPROPERTY(name, 'IsSingleUser') = 0
AND HAS_DBACCESS(name) = 1
AND state_desc = 'ONLINE'
AND CASE WHEN state_desc = 'ONLINE'
THEN OBJECT_ID(QUOTENAME(name) + '.[dbo].[CERTAIN_TABLE]', 'U')
END IS NOT NULL
However, what is still missing is a constraint that the query should only select databases where the table CERTAIN_TABLE has a specific column. How can this be achieved?
When i want to loop through all databases, i do a loop like the following. Its easy to follow:
DECLARE #dbs TABLE ( dbName NVARCHAR(100) )
DECLARE #results TABLE ( resultName NVARCHAR(100) )
INSERT INTO #dbs
SELECT name FROM sys.databases
DECLARE #current NVARCHAR(100)
WHILE (SELECT COUNT(*) FROM #dbs) > 0
BEGIN
SET #current = (SELECT TOP 1 dbName FROM #dbs)
INSERT INTO #results
EXEC
(
'IF EXISTS(SELECT 1 FROM "' + #current + '".INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = ''Target_Table_Name'' AND COLUMN_NAME = ''Target_Column_Name'')
BEGIN
--table and column exists, execute query here
SELECT ''' + #current + '''
END'
)
DELETE FROM #dbs
WHERE dbName = #current
END
SELECT * FROM #results
You are going to need either some looping or dynamic sql for this. I really dislike loops so here is how you could do this with dynamic sql.
declare #TableName sysname = 'CERTAIN_TABLE'
, #ColumnName sysname = 'CERTAIN_COLUMN'
declare #SQL nvarchar(max) = ''
select #SQL = #SQL + 'select DatabaseName = ''' + db.name + ''' from ' + QUOTENAME(db.name) + '.sys.tables t join ' + QUOTENAME(db.name) + '.sys.columns c on c.object_id = t.object_id where t.name = ''' + QUOTENAME(#TableName) + ''' and c.name = ''' + QUOTENAME(#ColumnName) + '''' + char(10) + 'UNION ALL '
from sys.databases db
where db.state_desc = 'ONLINE'
order by db.name
select #SQL = substring(#SQL, 0, len(#SQL) - 9)
select #SQL
--uncomment the line below when you are comfortable the query generated is correct
--exec sp_executesql #SQL

Dynamic query and use of variables

The query below works if the 2 dates are hard coded, however, I would like to replace them with the 2 variables #FirstDayM and #LastDayM.
When I do so, it returns the following error
"Conversion failed when converting date and/or time from character string"
DECLARE #sql varchar(max)
DECLARE #FirstDayM DATE = DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE())-1, 0)
DECLARE #LastDayM DATE = DATEADD(MONTH, DATEDIFF(MONTH, -1, GETDATE())-1, -1);
SELECT #sql = Coalesce(#sql + ' UNION ALL ', '') + 'SELECT COUNT(C1CustID) AS CertsCount, ''' + QuoteName(name)+ ''' as DBname FROM ' + QuoteName(name) + '.dbo.T_C1CustCourse'+
' WHERE C1CertificationDate_N >= '+'''2018-01-01'''+' AND C1CertificationDate_N <= '+'''2018-01-31'''
FROM sys.databases WHERE database_id > 4 AND state = 0;
EXEC (#sql);
Use sp_executesql. Always. It makes it easy to put parameters into queries. Even if the dynamic query does not start with the parameter, you might decide to add one later.
declare #sql nvarchar(max);
declare #firstDayM date;
declare #lastDayM date;
set #firstDayM = ?;
set #lastDayM = ?;
SELECT #sql = Coalesce(#sql + ' UNION ALL ', '') + '
SELECT COUNT(C1CustID) AS CertsCount, ''' + QuoteName(name)+ ''' as
DBname
FROM ' + QuoteName(name) + '.dbo.T_C1CustCourse' + '
WHERE C1CertificationDate_N >= #FirstDayM AND C1CertificationDate_N
<= #LastDayM'
FROM sys.databases
WHERE database_id > 4 AND state = 0;
EXEC sp_executesql #sql, N'#FirstDayM date, #lastDayM date',
#FirstDayM=#FirstDayM, #lastDayM=#lastDayM;

Inserting data into a table from a Dynamic SQL script

I am trying to run this script:
DECLARE #Client VARCHAR(50)
DECLARE #SQL VARCHAR(MAX)
DECLARE #DBReporting VARCHAR(500)
DECLARE #DBSignet VARCHAR(500)
DECLARE #databasename varchar(100)
SET #SQL = ''
DECLARE db_cursor CURSOR FOR
SELECT name
FROM sys.databases
WHERE name like '%reporting%'
AND NOT Name Like '%UAT%'
AND NOT Name Like '%Test%'
AND NOT Name Like '%Demo%'
AND NOT Name like '%staging%'
AND NOT Name like '%server%'
AND state_desc <> 'offline'
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #databasename
WHILE ##FETCH_STATUS = 0
BEGIN
SET #Client = REPLACE(REPLACE(#databasename, 'SourcingPlatform_', ''), '_Reporting', '')
SET #DBSignet = 'SourcingPlatform_' + #Client + '_Signet_Tradeflow'
SET #DBReporting = 'SourcingPlatform_' + #Client + '_Reporting'
SET #SQL = #SQL + 'INSERT INTO STS_Branding.[dbo].[S2C_KeyStats]
([Project]
,[DataDate]
,[EventTypeName]
,[CountOfAllEvents]
,[CreatedWithinLast3Months]
,[CreatedWithinLast6Months]
,[CreatedWithinLast12Months])
VALUES
SELECT ''' + #Client + ''' AS Client, convert(date, getdate()), EventTypeName collate Latin1_General_CI_AS,
count(id) as CountOfAllEvents,
(select COUNT(e3.ID)
from ' + #DBReporting + '..REPORTS_Sourcing_Event E3
where DATEDIFF(month,CreateDate, GETDATE()) <= 3
and E.EventTypeName = E3.EventTypeName) as CreatedLast3Months,
(select COUNT(e6.ID)
from ' + #DBReporting + '..REPORTS_Sourcing_Event E6
where DATEDIFF(month,CreateDate, GETDATE()) > 3
and DATEDIFF(month,CreateDate, GETDATE()) <= 6
and E.EventTypeName = E6.EventTypeName) as CreatedLast6Months,
(select COUNT(e12.ID)
from ' + #DBReporting + '..REPORTS_Sourcing_Event E12
where DATEDIFF(month,CreateDate, GETDATE()) > 6
and DATEDIFF(month,CreateDate, GETDATE()) <= 12
and E.EventTypeName = E12.EventTypeName) as CreatedLast12Months,
(select COUNT(e13.ID)
from ' + #DBReporting + '..REPORTS_Sourcing_Event E13
where DATEDIFF(month,CreateDate, GETDATE()) > 12
and E.EventTypeName = E13.EventTypeName) as CreatedOver12Months
FROM ' + #DBReporting + '..REPORTS_Sourcing_Event E
Group By EventTypeName
UNION '
FETCH NEXT FROM db_cursor INTO #databasename
END
CLOSE db_cursor
DEALLOCATE db_cursor
SET #sql = substring(#sql, 0, LEN(#sql) - len('UNION ')) + ' ORDER BY Client, EventTypeName collate Latin1_General_CI_AS'
--PRINT #SQL
exec(#SQL)
However, I am getting a syntax error.
I have printed the #SQL variable and the code generated looks good to me. Am I missing something really simple here? or am I way off what I want to achieve?
What I want to achieve is a script that goes through each DB referenced in the first select and get the values and insert them into my table.
Let me know if you need anymore information to help me, any help at all at this point would be greatly appreciated.
You should post the generated query, but I think it looks something like:
INSERT INTO STS_Branding.[dbo].[S2C_KeyStats]
([Project]
,[DataDate]
,[EventTypeName]
,[CountOfAllEvents]
,[CreatedWithinLast3Months]
,[CreatedWithinLast6Months]
,[CreatedWithinLast12Months])
VALUES -- Remove this, it's incorrect in combination with SELECT
SELECT (lots of selects)
UNION
INSERT INTO STS_Branding.[dbo].[S2C_KeyStats]
([Project]
,[DataDate]
,[EventTypeName]
,[CountOfAllEvents]
,[CreatedWithinLast3Months]
,[CreatedWithinLast6Months]
,[CreatedWithinLast12Months])
SELECT (lots of selects)
This is obviously not possible, you want to union the selects not the insert. So you should begin by initialling #SQL with the insert statement (outside the cursor). Inside the cursor you can use SET #SQL = #SQL + ... as you are already doing, but without the insert statement.
Also, please note substring is 1 based in SQL, not 0 as in, for example, C#.

Select table and column dynamically based on other table rows

I have following table and values,
Tb_name column_name1 column_name2
Citator_KTLO_CC Date_Created Date_Modified
Citator_KTLO_QA Date_Created Date_Modified
I want to select dynamically column from table, so the result is like this:
Select Date_Created,Date_Modified from Citator_KTLO_CC
and in next loop it will select for second row, like
Select Date_Created,Date_Modified from Citator_KTLO_QA
How can i do this by using dynamic sql ?
any example are appreciated.
here is an example of how to do this.
Since you dont post many info I just assume that the table containing all the tablenames is called 'tables'
Also this will only work if all tables have the same column types.
-- create a test table you dont need this
create table tables (tb_name varchar(100) primary key, field1 varchar(100), field2 varchar(100))
-- fill my test table you dont need this
insert into tables values ('table1', 'field1', 'field2')
insert into tables values ('table2', 'foo1', 'foo2')
insert into tables values ('table3', 'test1', 'test2')
-- this is the actual code you need, replace the names with your real names
declare #sql varchar(max) = ''
declare #tb_name varchar(100) = ''
declare #field1 varchar(100) = ''
declare #field2 varchar(100) = ''
declare myCursor cursor for
select tb_name, field1, field2 from tables -- dont know how your table is called
open myCursor
fetch next from myCursor into #tb_name, #field1, #field2
while ##FETCH_STATUS = 0
begin
set #sql = #sql + ' select ' + #field1 + ', ' + #field2 + ' from ' + #tb_name + ' union all '
fetch next from myCursor into #tb_name, #field1, #field2
end
close myCursor
deallocate myCursor
select #sql = left(#sql, len(#sql) - 10)
exec (#sql)
EDIT:
using a where clause is possible but things will get more complicated
declare #something date = getdate()
set #sql = #sql + ' select ' + #field1 + ', ' + #field2 + ' from ' + #tb_name + ' where ' + #field1 + ' = ' + #something + ' union all '
You can use the example above to build what you need just play with it.
EDIT:
using a where clause with a date format
declare #something date = getdate()
set #sql = #sql + ' select ' + #field1 + ', ' + #field2 + ' from ' + #tb_name + ' where ' + #field1 + ' = ''' + CONVERT(varchar(8), #something, 112) + ''' union all '
DECLARE #SQL VARCHAR(1000);
SET #SQL = '
SELECT *
FROM Citator_KTLO_CC
UNION ALL
SELECT *
FROM Citator_KTLO_QA;'
EXEC (#SQL);
How about something like this. If you've more than two cols, you can use dynamic sql to generate a list of cols to then generate more dynamic sql instead of hard coding.
DROP TABLE #Test
CREATE TABLE #Test
(Tb_name NVARCHAR(15),
column_name1 NVARCHAR(12),
column_name2 NVARCHAR(13));
INSERT INTO #Test VALUES
('Citator_KTLO_CC','Date_Created','Date_Modified'),
('Citator_KTLO_QA','Date_Created','Date_Modified');
DECLARE #SQL NVARCHAR(MAX)
SET #SQL = (SELECT STUFF((SELECT ' UNION ALL SELECT ' + Cols + ' FROM '+TbL
FROM (SELECT QUOTENAME(Tb_name) TBL,
QUOTENAME(column_name1) + ', '+
QUOTENAME(column_name2) Cols
FROM #Test) Blah
FOR XML PATH('')),1,10,''))
PRINT #SQL
EXEC sys.sp_executesql #SQL
Try this..
For selecting one row if you are running in aloop
DECLARE #sql NVARCHAR(4000)
SELECT #sql = ' select ' + column_name_1 + ',' + column_name2 + ' from ' + Tb_name
FROM < yourtable >
EXEC (#sql)
OR
DECLARE #sql NVARCHAR(4000)
SELECT #sql = 'union all select ' + column_name_1 + ',' + column_name2 + ' from ' + Tb_name
FROM < yourtable >
SET #sql =stuff(#sql,1,10,'')
EXEC (#sql)
DECLARE #ColumnList1 VARCHAR(MAX) = '''''';
DECLARE #ColumnList2 VARCHAR(MAX) = '''''';
DECLARE #ColumnNameFromTable1 VARCHAR(50);
DECLARE #ColumnNameFromTable2 VARCHAR(50);
DECLARE MyCursor1 CURSOR
FOR
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'Citator_KTLO_CC'
ORDER BY ORDINAL_POSITION
DECLARE MyCursor2 CURSOR
FOR
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'Citator_KTLO_QA'
ORDER BY ORDINAL_POSITION
OPEN MyCursor1
OPEN MyCursor2
FETCH NEXT FROM MyCursor1 INTO #ColumnNameFromTable1;
WHILE ##FETCH_STATUS = 0
BEGIN
FETCH NEXT FROM MyCursor2 INTO #ColumnNameFromTable2;
SET #ColumnList1 = #ColumnList1 + ',' + #ColumnNameFromTable1
SET #ColumnList2 = #ColumnList2 + ',' + #ColumnNameFromTable2
FETCH NEXT FROM MyCursor1 INTO #ColumnNameFromTable1;
END
CLOSE MyCursor1;
DEALLOCATE MyCursor1;
CLOSE MyCursor2;
DEALLOCATE MyCursor2;
EXEC ('SELECT ' + #ColumnList1 + ' FROM Citator_KTLO_CC UNION ALL SELECT ' +
#ColumnList2 + ' FROM Citator_KTLO_QA ')

List tables with recently modified records

I have a field namely Modified_Dt of type Datetime in all of my tables, to keep track of last modified date and time for a record.
Now, let's say I need to know which tables has records that has been modified recently(like today).
How do I write a query for that? How do I query multiple tables?
By the way, I am using MS SQL Server 2008 R2.
USE MASTER
GO
DECLARE #ObjectName NVARCHAR(255)
DECLARE TablesList CURSOR
FOR select object_name(object_id, db_id('DBStackExchange'))
from [DBStackExchange].sys.columns
where name = 'Modified_Dt'
OPEN TablesList
FETCH NEXT FROM TablesList INTO #ObjectName
WHILE ##FETCH_STATUS = 0
BEGIN
exec
( 'If exists ( SELECT 1 FROM DBStackExchange.dbo.[' + #ObjectName
+ ']
Where convert(varchar(20),Modified_Dt,103)>=convert(varchar(20),getdate(),103))
Print ''' + #ObjectName + '''
'
)
FETCH NEXT FROM TablesList INTO #ObjectName
END
CLOSE TablesList
DEALLOCATE TablesList
Note: Replace 'DBStackExchange' with your Database name
declare #T table (T_Name nvarchar(255), M datetime)
declare #T_Name nvarchar(255), #SQLT nvarchar(max)
declare c cursor for select name from sys.tables
open c
fetch next from c into #T_Name
while ##fetch_status = 0 begin
set #SQLT = 'select top 1 ''' + #T_Name + ''', Modified_Dt from ' + #T_Name + ' order by Modified_Dt desc'
insert #T
exec sp_executesql #SQLT
fetch next from c into #T_Name
end
close c
deallocate c
select * from #T where M >= dateadd(day,datediff(day,0,getdate()),0)
Here is an answer without cursor or temporary table
DECLARE #ColumnName AS nvarchar(40) = 'Modified_Dt';
DECLARE #ModifiedSince AS datetime = '20140709';
DECLARE #sql AS nvarchar(max) = '';
-- Build a query with UNION ALL between all tables containing #ColumnName
WITH AllTables AS (
SELECT SCHEMA_NAME(Tables.schema_id) AS SchemaName
,Tables.name AS TableName
,Columns.name AS ColumnName
FROM sys.tables AS Tables
INNER JOIN sys.columns AS Columns
ON Tables.object_id = Columns.object_id
WHERE Columns.name = #ColumnName
)
SELECT #sql = #sql +
'UNION ALL SELECT ' + QUOTENAME(TableName, '''') +
', ' + QUOTENAME(ColumnName) +
' FROM ' + QUOTENAME(TableName) + CHAR(13)
FROM AllTables;
-- Create a query which selects last change from all tables
SET #sql =
'WITH AllChanges(TableName, ModifiedTime) AS ( ' +
STUFF(#sql, 1, LEN('UNION ALL'), '') + -- Remove first UNION
') ' +
'SELECT TableName ' +
' ,MAX(ModifiedTime) ' +
'FROM AllChanges ' +
'WHERE ModifiedTime > #ModifiedSince '
'GROUP BY TableName '
EXECUTE sp_executesql #sql, N'#ModifiedSince datetime', #ModifiedSince