SQL Server 2008: Bulk Datatype Change - sql

I have an SQL Server 2008 database with many tables. I've been using the now lame datetime datatype and want to use the new and better datetime2. In most places where I have a datetime field, the corresponding column name is Timestamp. Is there anywhere to do a bulk change from datatime to datetime2?

Run this in Management Studio, copy the result and paste into new Query Window:
select 'ALTER TABLE ' + OBJECT_NAME(o.object_id) +
' ALTER COLUMN ' + c.name + ' DATETIME2 ' +
CASE WHEN c.is_nullable = 0 THEN 'NOT NULL' ELSE 'NULL' END
from sys.objects o
inner join sys.columns c on o.object_id = c.object_id
inner join sys.types t on c.system_type_id = t.system_type_id
where o.type='U'
and c.name = 'Timestamp'
and t.name = 'datetime'
order by OBJECT_NAME(o.object_id)

Data type alteration generally requires ALTER TABLE statements:
ALTER TABLE myTable ALTER COLUMN timestamp datetime2 [NOT] NULL
To change all the datetime columns into datetime2 in a given database & schema:
DECLARE #SQL AS NVARCHAR(4000)
DECLARE #table_name AS NVARCHAR(255)
DECLARE #column_name AS NVARCHAR(255)
DECLARE #isnullable AS BIT
DECLARE CUR CURSOR FAST_FORWARD FOR
SELECT c.table_name,
c.column_name,
CASE WHEN c.is_nullable = 'YES' THEN 1 ELSE 0 END AS is_nullable
FROM INFORMATION_SCHEMA.COLUMNS c
WHERE c.data_type = 'datetime'
AND c.table_catalog = 'your_database'
AND c.table_schema = 'your_schema'
-- AND c.table_name = 'your_table'
OPEN CUR
FETCH NEXT FROM CUR INTO #table_name, #column_name, #isnullable
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #SQL = 'ALTER TABLE ' + #table_name + ' ALTER COLUMN ' + #column_name + ' datetime2' + (CASE WHEN #isnullable = 1 THEN '' ELSE ' NOT' END) + ' NULL;'
EXEC sp_executesql #SQL
FETCH NEXT FROM CUR INTO #table_name, #column_name, #isnullable
END
CLOSE CUR;
DEALLOCATE CUR;

This would be a bit of a brute-force method, but you could always look up all columns of datatype datetime using the sys.columns view, grab the table name and column name, iterate over that list with a cursor, and for each entry generate an ALTER TABLE statement like so:
ALTER TABLE #tablename ALTER COLUMN #columnname datetime2
Then run said statement with EXEC. Obviously, you'd need to have permissions both to query sys.columns and to ALTER all of those tables...
Apologies there isn't more code in this answer - don't have a copy of SSMS on this machine, and can't remember the syntax for all of that from memory. :)

I would use a query window, and output all of the ALTER TABLE statements you need to perform this. Once you have them all generated, you can run the result against the database.
if you select from SYSCOLUMNS the names of the tables and fields that you want, you can generate the statements you need to change all of the columns in the database to datetime2.
ALTER TABLE {tablename} ALTER COLUMN {fieldname} datetime2 [NULL | NOT NULL]

You can do something like this:
SELECT
'ALTER TABLE [' + Table_Schema+'].['+Table_Name
+'] Alter Column ['+Column_Name+'] datetime2;'
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE DATA_TYPE='datetime';
Now you have all the scripts necessary to make your bulk type change.
Ref: https://www.sqlservercentral.com/forums/topic/how-to-change-column-type-on-all-tables-of-a-certain-database

Related

SQL Server - changing definition of column that appears in multi tables

many of tables in my DB have a Boolean column 'IsDeleted'.
I need to alter the column in all tables, that the default value will be zero, and then update all old records with value null, to be with value zero.
There is a way to do it beside writing a script for every table?
Thanks,
This would be a good starting point to generate the CReate, Update and Rename scripts required. Advisory: TEST ON BACKUP OF DATABASE FIRST.
select
'ALTER TABLE dbo.' + O.Name + ' ADD IsDeletedNew bit default 0;
UPDATE dbo.' + O.Name + ' SET IsDeletedNew = 1 WHERE IsDeleted = 1;
UPDATE dbo.' + O.Name + ' SET IsDeletedNew = 0 WHERE IsDeleted = 0 OR IsDeleted IS NULL;
ALTER TABLE dbo.' + O.Name + ' DROP COLUMN IsDeleted;
EXECUTE sp_rename N''dbo.' + O.Name + '.IsDeletedNew'', N''Tmp_IsDeleted_1'', ''COLUMN''
EXECUTE sp_rename N''dbo.' + O.Name + '.Tmp_IsDeleted_1'', N''IsDeleted'', ''COLUMN'' '
from syscolumns C
Inner join sysobjects o on C.ID = O.ID
where c.name = 'IsDeleted'
First, I can set a default value for a boolean filed. It worked for me.
ALTER TABLE [dbo].<TableName> ADD DEFAULT 0 FOR IsDeleted
This is my script that sets default value for every 'IsDeleted' field that doesn't have a defualt value. It worked for me.
DECLARE #NAME VARCHAR(100)
DECLARE #SQL NVARCHAR(300)
DECLARE CUR CURSOR
FOR
SELECT t.name AS 'TableName'
FROM sys.columns c
JOIN sys.tables t ON c.object_id = t.object_id
WHERE c.name = 'IsDeleted'
AND (SELECT object_definition(default_object_id) AS definition
FROM sys.columns
WHERE name ='IsDeleted'
AND object_id = object_id(t.name)) is null
OPEN CUR
FETCH NEXT FROM CUR INTO #NAME
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQL = 'ALTER TABLE [dbo].'+#NAME+' ADD DEFAULT 0 FOR IsDeleted'
--PRINT #SQL -- will print all the update scripts
EXEC Sp_executesql #SQL
FETCH NEXT FROM CUR INTO #NAME
END
CLOSE CUR
DEALLOCATE CUR
With so many tables, do the alter using dynamic SQL
declare #tab_name varchar(120)
declare #the_sql varchar(1000)
declare MyCursor cursor
for
select distinct table_name
from INFORMATION_SCHEMA.COLUMNS
where column_name = 'IsNumeric'
open MyCursor
fetch next from MyCursor into #tab_name
while ##fetchstatus = 0
begin
set #the_sql = 'alter table ' + #tab_name + ' add NewNumeric bit default 0'
execute (#the_sql)
fetch next from MyCursor into #tab_name
end
close MyCursor
deallocate MyCursor
rinse and repeat to updatethe values, delete the old column and then update the new column

SQL update in a select statement

my problem is: I need to select all my db-tables which contain a column NrPad out of my database and for exactly this tables I need to update the column NrPad
I have already a working select and update statement:
select
t.name as table_name
from sys.tables t
inner join sys.columns c
on t.object_id = c.object_id
where c.name like 'NrPad'
Update Anlage Set NrPad = CASE WHEN Len(Nr) < 10 THEN '0' + Convert(Nvarchar,Len(Nr)) ELSE Convert(Nvarchar,Len(Nr)) END + Nr
My problem is: How can I merge this two statements together?
I'm open to suggestions and your help is greatly appreciated.
Use the INFORMATION_SCHEMA rather than sys.tables, and create a dynamic SQL statement like so:
DECLARE #sql varchar(max) = '';
SELECT
#sql = #sql + '; UPDATE ' + c.TABLE_NAME + ' SET NrPAd = CASE WHEN LEN(Nr)<10 THEN ''0'' + CONVERT(NVARCHAR,LEN(NR)) ELSE CONVERT(NVARCHAR,LEN(NR)) END + Nr'
FROM INFORMATION_SCHEMA.COLUMNS c
where c.COLUMN_NAME = 'NrPad'
print #sql -- for debugging purposes
exec (#sql)
This assumes that all tables that have the NrPad column also have a Nr column. If you need to check for those, or if you just need to use the Nr column from a particular table, it's a bit different (either join against INFORMATION_SCHEMA.COLUMNS again or against Anglage to get the value of Nr or check that Nr is a column on that table).
Not testet on your case but you could do an update - set - from - where.
Have a look at this question with multiple answers: How do I UPDATE from a SELECT in SQL Server?
maybe someone will judge me,but all i can do for this case is cursor
DECLARE #table_name varchar(100)
DECLARE #sql varchar(1000)
DECLARE table_cursor CURSOR FOR
select
t.name as table_name
from sys.tables t
inner join sys.columns c
on t.object_id = c.object_id
where c.name like 'NrPad'
OPEN table_cursor
Fetch next From table_cursor Into #table_name
While ##fetch_status=0
Begin
set #sql = 'Update' + #table_name + 'Set NrPad = CASE WHEN Len(Nr) < 10 THEN '0' + Convert(Nvarchar,Len(Nr))
ELSE Convert(Nvarchar,Len(Nr)) END + Nr'
EXEC (#sql)
Fetch Next From table_cursor Into #table_name
End
Close table_cursor
Deallocate table_cursor
this is how you write the cursor in SQLSERVER, i really don't want code another one for Oracle. so please tag the dbms you are using next time
You can modify the select statement to generate the update statements then execute them all.
Below uses string literal of Oracle.
select 'Update ' || t.name || q'[ Set NrPad = CASE WHEN Len(Nr) < 10 THEN '0' + Convert(Nvarchar,Len(Nr)) ELSE Convert(Nvarchar,Len(Nr)) END + Nr;]'
from sys.tables t
inner join sys.columns c
on t.object_id = c.object_id
where c.name like 'NrPad'

How can I easily convert all ntext fields to nvarchar(max) in SQL query?

I would love to be able to write a SQL query, without enumerating the columns, which will return me all the columns, and any ntext column converted to varchar(max). I was wondering if there is a clever way to do this.
This would be great because then I could do the comparison based operators such as UNION, EXCEPT etc. on such queries. The netxt column is not comparable so it fails when using those operators.
My current idea:
Create a function to build the query as dynamic sql. Something similar to this: http://lotsacode.wordpress.com/2010/03/23/sql-server-ntext-cannot-be-selected-as-distinct/
Is there a better way?
Thanks for your input!
NTEXT will be removed from future versions of SQl-Server anyway (along with Image and text), so why not just bite the bullet and change your columns to NVARCHAR(MAX)? It may be costly once, but it will probably be worth it:
ALTER TABLE dbo.T ALTER COLUMN NTextColumn NVARCHAR(MAX) NULL; -- OR NOT NULL
You can generate and execute the script for an entire database using this:
DECLARE #SQL NVARCHAR(MAX) =
( SELECT 'ALTER TABLE ' + QUOTENAME(OBJECT_SCHEMA_NAME(object_id)) + '.' +
QUOTENAME(OBJECT_NAME(object_id)) +
' ALTER COLUMN ' + QUOTENAME(Name) +
' NVARCHAR(MAX) ' +
CASE WHEN is_nullable = 0 THEN 'NOT' ELSE '' END +
' NULL;' + CHAR(13) + 'GO' + CHAR(13)
FROM sys.columns
WHERE system_type_id = 99 --NTEXT
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)');
EXECUTE sp_executesql #SQL;
I used this cursor (and here, there's no set-based alternative, I'm afraid) to do just that:
DECLARE TableCursor CURSOR FAST_FORWARD
FOR
SELECT
t.Name,
c.name,
c.is_nullable,
typ.user_type_id
FROM
sys.columns c
INNER JOIN
sys.tables t ON c.object_id = t.object_id
INNER JOIN
sys.types typ ON c.system_type_id = typ.system_type_id
WHERE
typ.name IN ('text', 'ntext') -- user_type_id: text = 35, ntext = 99
DECLARE #TableName sysname, #ColumnName sysname, #IsNullable BIT, #TypeID INT
OPEN TableCursor
FETCH NEXT FROM TableCursor INTO #TableName, #ColumnName, #IsNullable, #TypeID
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #Stmt NVARCHAR(999)
SET #Stmt = 'ALTER TABLE dbo.[' + #TableName + '] ALTER COLUMN [' + #ColumnName + '] ' +
CASE #TypeID
WHEN 35 THEN ' VARCHAR(MAX) '
WHEN 99 THEN ' NVARCHAR(MAX) '
END +
CASE WHEN #IsNullable = 1 THEN 'NULL' ELSE 'NOT NULL' END
PRINT #Stmt
EXEC (#Stmt)
FETCH NEXT FROM TableCursor INTO #TableName, #ColumnName, #IsNullable, #TypeID
END
CLOSE TableCursor
DEALLOCATE TableCursor
I simplified my code a bit by assuming that all my tables are in the dbo schema - if that's not the case for you, you'd have to include the schema from the sys.schema catalog view, too.
Running this code will turn all text into varchar(max) and all ntext into nvarchar(max) once and for all, and all your issues with text/ntext are gone forever! :-)
Here is my modified version of GarethD's answer above. Had issues with SQL not finding some tables, so I used sys.tables joined with sys.columns. Also, the is_nullable line was incorrect (if the field is not nullable, then you set it to NOT NULL).
DECLARE #SQL NVARCHAR(MAX) = ' ';
SELECT #SQL = #SQL + ' ALTER TABLE ' + QUOTENAME(OBJECT_SCHEMA_NAME(sys.columns.object_id)) + '.' +
QUOTENAME(OBJECT_NAME(sys.columns.object_id)) +
' ALTER COLUMN ' + QUOTENAME(sys.columns.Name) +
' NVARCHAR(MAX) ' +
CASE WHEN is_nullable = 0 THEN 'NOT NULL' ELSE '' END
FROM sys.Tables
inner join sys.columns on sys.tables.object_id = sys.columns.object_id
WHERE sys.columns.system_type_id = 99 ; --NTEXT
EXECUTE sp_executesql #SQL;
GO

Search sql database for a column name, then search for a value within the retuned columns

This query will search a database for a specific column name. I would like to go one step further and search the returned columns for a specific value.
SELECT t.name AS table_name,
SCHEMA_NAME(schema_id) AS schema_name,
c.name AS column_name,
FROM sys.tables AS t
INNER JOIN sys.columns c ON t.OBJECT_ID = c.OBJECT_ID
WHERE c.name LIKE '%Example%'
Any ideas?
Many thanks
For example, I have a database named Organisation. I have more than one table where tax_id column is present.
Most of the time, we have to find such a column from the whole database.
The solution is provided below:
select table_name,column_name from information_schema.columns
where column_name like '%tax%'
There is no matter in query to database name which ever you just need to change willing Column Name and will found required result
Search any value Like computer in whole database in which column and in which tables value computer exists
For it first we need to write a store procedure then we reuse it for our search i got it from http://vyaskn.tripod.com/search_all_columns_in_all_tables.htm very perfect result.
after executing store procedure we got required result as in given below image.
Image showing complete search result of keyword computer from whole database.
Above was concept to solve it.Exact Query fullfilling above requirment is below
Select tax_id from (select table_name from information_schema.columns
where column_name = 'tax_id') as temp
There is not such system table present for this kind of searching. Whereas you can try this for your purpose
DECLARE #ValueToSearch NVARCHAR(500)
DECLARE #SearchColumn NVARCHAR(100)
DECLARE #TableName NVARCHAR(200)
DECLARE #ColumnName NVARCHAR(200)
SET #ValueToSearch ='YOUR VALUE TP SEARCH'
SET #SearchColumn = 'YOUR COLUMN'
DECLARE #getResult CURSOR
SET #getResult = CURSOR FOR
SELECT t.name AS table_name,c.name AS column_name FROM sys.tables AS t INNER JOIN sys.columns c ON t.OBJECT_ID = c.OBJECT_ID WHERE c.name = #SearchColumn
OPEN #getResult
FETCH NEXT FROM #getResult INTO #TableName,#ColumnName
WHILE ##FETCH_STATUS = 0
BEGIN
SET NOCOUNT ON ;
DECLARE #RESULT INT;
DECLARE #TYPE INT
DECLARE #QUERY NVARCHAR(1000)
SET #QUERY = 'select #RESULT=count(*) from ' + ISNULL(#TableName,'') +' WHERE '+ ISNULL(#ColumnName,'')+'='''+ ISNULL(#ValueToSearch,'') +''''
EXEC sp_executesql #QUERY,
N'#result int OUTPUT, #type int OUTPUT',
#RESULT OUTPUT,
#TYPE OUTPUT
IF(ISNULL(#RESULT,0)>0)
BEGIN
SET NOCOUNT ON;
SELECT ' COLUMN '+ #ColumnName + ' OF TABLE ' +#TableName+ ' HAS THIS VALUE.'
END
FETCH NEXT FROM #getResult INTO #TableName,#ColumnName
END
CLOSE #getResult
DEALLOCATE #getResult
Thanks
Manoj

Change the precision of all decimal columns in every table in the database

I have a rather large database that has alot of decimal columns in alot of tables, the customer has now changed their mind and wants all the numbers (decimals) to have a precision of 3 d.p. instead of the original two. Is there any quick way of going through all the tables in a database and changing any decimal column in that table to have 3.d.p instead of 2 d.p?
The db is on sql 2005.
Any help would be great.
Get the columns from information_schema based on type and scale, then alter them to have the desired scale.
declare #col sysname
declare #tbl sysname
declare #sql nvarchar(256)
declare crsFix cursor for
select table_name, Column_name from information_schema.columns
where data_type = 'decimal' and Numeric_Scale = 3
open crsFix
fetch next from crsFix into #tbl, #col
while(##Fetch_Status = 0)
Begin
set #sql = 'Alter table [' + #tbl + '] alter column [' + #col + '] decimal(38,2) '
print #sql
exec sp_executesql #sql
fetch next from crsFix into #tbl, #col
End
close crsFix
deallocate crsFix
If you can get the table and column names this shouldn't be so bad
ALTER TABLE MyTable ALTER COLUMN MyColumn DECIMAL(#,#)
Based on #cmsjr suggestion and other help from stackoverflow i came up with the following tsql that list all the columns whose datatype is numeric and generates a script for each and every column that we need to modify.
SELECT c.TABLE_NAME, c.column_name, c.COLUMN_DEFAULT, c.IS_NULLABLE, c.NUMERIC_PRECISION, c.NUMERIC_SCALE
, 'ALTER TABLE ' + c.TABLE_NAME + ' ALTER COLUMN ' + c.column_name + ' NUMERIC (18,5) ' + CASE c.IS_NULLABLE WHEN 'NO' THEN ' NOT NULL' ELSE ' NULL' END AS script
FROM INFORMATION_SCHEMA.columns cs
INNER JOIN INFORMATION_SCHEMA.tables t ON t.table_name = c.table_name
WHERE c.data_type like 'numeric' AND t.table_type = 'base table'
--AND c.NUMERIC_PRECISION in (9,18) AND c.NUMERIC_SCALE = 2