MSSQL: alter column not null, in general - sql-server

In Microsoft SQL Server 2008 R2, I would like to change a nullable column to not null. Obviously, I could do this by restating the datatype, as
alter table t alter column c int not null
if the column t.c is of int datatype, for example. But what about in general, without restating the existing data type? I'm looking for some equivalent of
alter table t alter column c not null
where the existing datatype is kept in place, and only the nullability is turned off.
Background
I have done an audit of my database and found many cases where a column is specified as nullable but no null values occur in practice. I'd like to tighten up the schema to forbid null values in these columns. Manually writing the DDL to 'alter column' for each one is error-prone because I might get the datatype wrong. I could automatically generate the code by using a schema-dumper program which outputs the existing datatype of each column, but that too has risks, if the dumper program is not aware of the latest datatypes and outputs something else (as an example, suppose it doesn't know about datetime2 and writes out datetime instead).
The SQL server already knows what the column type is, so surely there is a way to tell it to keep that and just turn off the nullability.
If there's really no way to do it except by finding the existing datatype to put it into the DDL, perhaps you could recommend a suitable tool to use? I know about dbschema.pl from Sybase days but there might be something more modern, or some well-known fragment of SQL that prints out the necessary statements from the schema views.

Two approaches:
1) Expand on this answer to include consideration for max_length,precision,scale, and collation_name. If you have multiple schemas, you will need to accommodate that too.
SELECT
'ALTER TABLE '
+QUOTENAME(aud.[table_name])
+' ALTER COLUMN '
+QUOTENAME(aud.[column_name])
+TYPE_NAME([system_type_id])
+' NOT NULL;'
FROM MyColumnAuditList aud
INNER JOIN sys.columns col ON (
col.[object_id] = OBJECT_ID(aud.[table_name]) AND
col.[name] = aud.[column_name]
)
2) In SSMS, right-click the Database and select 'Script Database As'. Use your text parsing tools of choice to extract the column definitions from the result.

The 'two approaches' answer suggested by Anon is helpful. The website's comment box doesn't allow enough text so I will post my final answer here.
The linked answer has special provision for user data types, which my database doesn't have, so I am using the type_name builtin instead. This query tries to reverse-engineer the type for each column:
select t.name,
c.name,
case
when type_name(c.system_type_id) in (
'int', 'real', 'float', 'date', 'time', 'datetime', 'datetime2',
'tinyint', 'smallint', 'smalldatetime', 'bit', 'bigint', 'timestamp',
'image'
) then type_name(c.system_type_id)
else type_name(c.system_type_id) + '('
+ case
when precision = 0 then convert(varchar(10), c.max_length)
else convert(varchar(10), precision) + ', ' + convert(varchar(10), scale)
end
+ ')'
end as ty
from sys.tables t
join sys.columns c
on t.object_id = c.object_id
where c.is_nullable = 1
and c.is_computed = 0
and t.schema_id = 1
order by t.name,
c.name
Then you can take each row from this query and do a check that no nulls exist before running 'alter table'. I am doing something like the following:
select case when
exists (select 0 from TABLE)
and not exists (select 0 from TABLE tablesample (1000 rows) where COLUMN is null)
then 1 else 0 end
for each TABLE, COLUMN returned by the first query. If the second query returns 1 then you can probably make the 'alter table' change. I use tablesample above to stop this being too heavy on the database, since I plan to run the check regularly; if the size of the table as returned by sp_spaceused is less than 100 kilobytes then I omit the tablesample clause.
Or, if you feel brave you could just run all the 'alter table' statements and let them fail if the column does contain nulls.
Oddly, I don't have permissions on the database to right-click in Management Studio and 'script database as', although I can do it for individual objects.

Related

T-SQL Check if the database contains certain tables and columns MSSQL

My goal is to continue in the procedure only with the database selected if it contains a specific table and a specific column.
So that I don't get an error message later when selecting that this column doesn't exist.
Background:
It is the case that database A table A has a more current status than database B with table A. In database B of table A a column is missing, which is the reason for the error that this column does not exist. Which I also do not want to add.
This is my attempt so far:
exec sp_MSforeachdb
'
use [?]
IF (''?'' NOT LIKE ''%example%'' AND ''?'' NOT LIKE ''%example_two%''
AND EXISTS(Select 1 from sys.tables where name = ''Table1'')
AND EXISTS(Select 1 from sys.tables where name = ''Table2'')
AND ''?'' NOT IN (SELECT * FROM Database.dbo.Blacklist)
)
BEGIN
IF(EXISTS(SELECT myColumn FROM Table1 Where ID = 5 AND XYZ = 3)) BEGIN.....'
Even when i switch instead of
..
AND EXISTS(Select 1 from sys.tables where name = ''Table1'')
AND EXISTS(Select 1 from sys.tables where name = ''Table2'')
..
To:
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = ''Table1''
AND COLUMN_NAME = ''MyExample''
it will not work the error log outputs: Incorrect syntax near 'TableXX'.
The error log gives me all databases that are practically checked, system databases as well.
Alternative it would also be helpful if someone knows how to use a case when in the select by trying to store an alternative value once the column does not exist e.g. like this:
'SELECT...
CASE WHEN exists(
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE
TABLE_NAME =''TableX'' AND COLUMN_NAME = ''ColumnX'')
THEN ''ST.ColumnX''
ELSE ''0 AS ColumnX''
END
FROM ...'
I just want to select databases that have the valid table and column, whenever I don't exist I take another table, this works until I find a table where a column doesn't exist, the column is however my select statement therefore I get an error, I want to focus on the alternative question, is there a way to check if the column exists in the current table? before assigning a value?
Case WHEN ColumnX exists THEN (ValueOfColumnX) ELSE 0 END AS Column.
Thank you in advance for any help
Use one single quotes when you are specifying the names of your tables and columns
SELECT 1 FROM your_databasename.INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'Table1'
AND COLUMN_NAME = 'MyExample'
If you want to check if a column exists in a table try looking into this thread

Executing dynamic SQL with return value as a column value for each rows

I have a rather simple query that I started to modify in order to remove temp table as we have concurrency issues over many different systems and clients.
Right now the simple solution was to break up the query in multiple separate queries to replicate what SQL was doing before.
I am trying to figure out a way to return the result of a dynamic SQL query as a column value. The new query is quite simple, it look in the system objects for all table with specific format and output. What i am missing is that for each record i need to output the result of a dynamic query on each of those table.
The query :
SELECT [name] as 'TableName'
FROM SYSOBJECTS WHERE xtype = 'U'
AND (CHARINDEX('_PCT', [name]) <> 0
OR CHARINDEX('_WHT', [name]) <> 0)
All these table have a common column called Result which is a float. What i am trying to do is return the count of this column under some WHERE clause that is generic and will work will all tables as well.
A desired query (i know it's not valid) would be :
SELECT [name] as 'TableName',
sp_executesql 'SELECT COUNT(*) FROM ' + [name] + ' WHERE Result > 0 OR (Result < 139 AND CurrentIndex < 15)' as 'ResultValue'
FROM SYSOBJECTS WHERE xtype = 'U'
AND (CHARINDEX('_PCT', [name]) <> 0
OR CHARINDEX('_WHT', [name]) <> 0)
Before it used to be easy. We had a temp table with 2 columns and were filling the table name first. Then we iterate on the temp table and execute the dynamic sql and return the value in an OUTPUT variable and simply update the record of the temp table and finally return the table.
I have tried a scalar function but it doesn't support dynamic SQL so it doesn't work. I would rather not create the 13,000~ different queries for the 13,000~ tables.
I have tried using a reference table and use trigger to update the status but it slow the system way to much. The average tables insert and delete 28 millions records. The original temp table query only took 5-6 minutes to execute due to very good indexing and now we are reaching 25-30 minutes.
Is there any other solution available than Querying the table list then the Client query each table one by one to know it status ?
We are using SQL Server 2017 if some new features are available now
You can use this script for your purpose (tested in SQL Server 2016).
Updated: It should work now as the results are a single set now.
EXEC sp_msforeachtable
#precommand = 'CREATE TABLE ##Statistics
(TableName varchar(128) NOT NULL,
NumOfRows int)',
#command1 ='INSERT INTO ##Statistics (TableName, NumOfRows)
SELECT ''?'' Table_Name, COUNT(*) Row_Count FROM ? WHERE Result > 0 OR (Result < 139 AND CurrentIndex < 15)',
#postcommand = 'SELECT TableName, NumOfRows FROM ##Statistics;
DROP TABLE ##Statistics'
,#whereand = ' And Object_id In (Select Object_id From sys.objects
Where name like ''%_PCT%'' OR name like ''%_WHT%'')'
For more details on sp_msforeachtable Please visit this link

Is it possible to scan through selected tables in a database and find out the missing columns in those tables?

I am looking for a way to scan through all tables in a database (or) selected tables in a database and find out the missing values (either null or empty) in columns of those tables? Can someone please tell me if this is possible? If so, how will the query look like
Welcome to SO. I agree that it would take some work to come up with a solution, but a simple starting point would be to use the system tables in SQL to create some dynamic SQL that you could -- then -- run against your db.
SELECT 'SELECT ''' + o.[name] + ''' as Tbl, ''' + c.[name] + ''' as Col,
COUNT(*) as Nulls FROM [' + o.[name] + ']
WHERE [' + c.[name] + '] IS NULL AND c.[isnullable]=1
HAVING COUNT(*) > 0'
FROM syscolumns c INNER JOIN sysobjects o ON c.[id]=o.[id]
WHERE o.[name]='[mytablename]'
This query uses the sysobjects and syscolumns tables. It returns a result set with 'SELECT' queries against every column in your [mytablename] table. Copy the results to a new window and execute -- and you'll get a count of nulls for each table/column.
Use caution though - if the tables are big, then those queries could cause serious performance issues (I assume you dont have indexes on each column, so you're basically doing gigantic table scans).
Edit - Added a quick check so that we only see NULLABLE values in the result set.

SQL Server ambiguous query validation

I have just come across a curious SQL Server behaviour.
In my scenario I have a sort of dynamic database, so I need to check the existence of tables and columns before run queries involving them.
I can't explain why the query
IF 0 = 1 -- Check if NotExistingTable exists in my database
BEGIN
SELECT NotExistingColumn FROM NotExistingTable
END
GO
executes correctly, but the query
IF 0 = 1 -- Check if NotExistingColumn exists in my ExistingTable
BEGIN
SELECT NotExistingColumn FROM ExistingTable
END
GO
returns Invalid column name 'NotExistingColumn'.
In both cases the IF block is not executed and contains an invalid query (the first misses a table, the second a column).
Is there any reason why SQL engine checks for syntax erorrs just in one case?
Thanks in advance
Deffered name resolution:
Deferred name resolution can only be used when you reference nonexistent table objects. All other objects must exist at the time the stored procedure is created. For example, when you reference an existing table in a stored procedure you cannot list nonexistent columns for that table.
You can look through the system tables for the existence of a specific table / column name
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 '%colname%'
AND t.name LIKE '%tablename%'
ORDER BY schema_name, table_name;
The query above will pull back all tables / columns with partial match of a columnname and tablename, just change the like % for exact match.

Search for column in entire SQL database

I need to change a column's data type but I do not every location in the database that might be referencing it. Is there a script or application that can search all views, stored procedures, functions, table computed columns, etc. within a database and show what is referencing the column? I am using SQL Server 2005 Standard.
Thanks,
Aaron
You can always inspect the sys.columns catalog view:
SELECT
c.NAME 'Col Name',
OBJECT_NAME(c.OBJECT_ID) 'Table Name',
t.name
FROM
sys.columns c
INNER JOIN
sys.types t ON c.system_type_id = t.system_type_id
WHERE
c.Name = 'your-column-name-here'
and based on that information, you can generate the ALTER statements for a database:
SELECT
'ALTER TABLE dbo.' + OBJECT_NAME(c.OBJECT_ID) +
' ALTER COLUMN ' + c.NAME ' NewDataType NULL'
FROM
sys.columns c
WHERE
c.Name = 'your-column-name-here'
This query generates a set of ALTER TABLE .... statements which you can then copy to a SSMS query window and execute.
Word of warning: if any of the columns are being referenced - in a foreign key relationship, or if there's a default or check constraint on them - this approach might fail. In that case, you'd need to do some extra steps for those columns (like drop the constraints first etc.)
Update: this searches for the columns as defined in tables.
If you need to search into stored procedures, view and functions as well, I would strongly recommend using Red-Gate's excellent and free (!!) SQL Search utility - excellent stuff!
I like using a free search add-in tool from redgate software. I'm amazed at how useful it is - you can find all references to text quickly with it.
This description is from SQL Curry:
SQL Search finds fragments of SQL text within stored procedures, functions, views and more and once you find them, it quickly allows you to click and jump to the objects, wherever they happen to be on your servers. It’s pretty cool!
Here is the link: SQL Search
This query will help you find any table's column and the column it is referring to -
SELECT OBJECT_NAME(f.parent_object_id) AS [Table], COL_NAME(fc.parent_object_id,fc.parent_column_id) AS [Column],
OBJECT_NAME (f.referenced_object_id) AS RefTable, COL_NAME(fc.referenced_object_id,fc.referenced_column_id) AS RefColumn,
f.name AS FK
FROM sys.foreign_keys AS f INNER JOIN sys.foreign_key_columns AS fc
ON f.OBJECT_ID = fc.constraint_object_id
WHERE OBJECT_NAME(f.parent_object_id) = '<your table name>'
AND COL_NAME(fc.parent_object_id,fc.parent_column_id) = '<your column name>'
I work for Red Gate and I see that SQL Search as already been mentioned. Glad that works for you. Another tool that can specifically list column dependencies is SQL Prompt 5, due to be released soon. You can download the EA build by visiting: http://www.surveymk.com/s.aspx?sm=zDJogAY5rwdIwOX/SqtTCQ%3d%3d and joining the early access list. I'd welcome you to try this out and let me know if it doesn't match your requirements. Another great feature it has is the ability to list your invalid objects. In other words, if you rename a column and you have a stored procedure that references the old column, it will draw your attention to this.

Resources