I am working on an application that we will be using to maintain our database dictionary (description of each column in each table) and would like to build a "Refresh" function that can go to the database and get the updated column list/names.
What I'm having issues with is when a column is renamed/moved, I would like to track if there is way to update the database with the new details. I tried using the Column_ID from syscolumns, but this changes when the column is moved so seems to be an arbitrary number.
Is there any unique ID that SQL Server generates for a column that doesn't change?
My alternative is to add the new column and mark the old one as removed.
Thanks
Why don't you store the description in the column's description property. Then when the column is updated the developer would be responsible for updating this.
I wrote a web font-end to this over 10 years ago.... I could send you the code if you want? Looked like:
You can try this... its a composite of two queries:
This will get you the list of coumns inside a table:
SELECT c.object_id, OBJECT_NAME(c.object_id) AS OBJECT_NAME
,c.NAME AS COLUMN_NAME
,t.NAME AS DATA_TYPE
,c.max_length AS MAX_LENGTH
FROM sys.all_columns c
INNER JOIN sys.types t ON t.system_type_id = c.system_type_id
WHERE object_name(c.object_id) = 'TableName'
You can generate a HashCode of the column name and combine it with object_id... which should be unique unless the column name changed
Hope this will help.
I would add a new column and mark the old one as removed.
[edit]
I thought that what SQL server deleted the table and create it in the first place, and after your question I went to investigate:
If you mark the option of "Prevent saving changes that require table re creation" (in Options -> Designers -> Table and database designers) and then create the following table:
CREATE TABLE [dbo].[testTbl](
[colA] [nchar](10) NULL,
[colB] [smallint] NOT NULL
) ON [PRIMARY]
and even insert some values:
INSERT INTO dbo.testTbl
VALUES ('text1', 12)
,('text2', 22)
,('text3', 32)
,('text4', 42)
,('text5', 52)
And then run the code from above you will get:
SELECT c.object_id, OBJECT_NAME(c.object_id) AS OBJECT_NAME
,c.NAME AS COLUMN_NAME
,t.NAME AS DATA_TYPE
,c.max_length AS MAX_LENGTH
FROM sys.all_columns c
INNER JOIN sys.types t ON t.system_type_id = c.system_type_id
WHERE object_name(c.object_id) = 'testTbl'
You will get the following values:
If you try to do now the following changes (in the table designer)you will find out that all require drop and re create from the database:
Change a NULLABLE column to non-nullable
change non-nullable column to a nullable
(which is to be expected; those changes require the columns to move around the page)
change the datatype of a column (nchar(10) to more or less, smallint to tinyint or int)
but if you want to change the column from B to C, SQL can manage it, and the results would be:
The object id refers to the table id, and therefore the table stayed with the same object id.
Related
I create a table by linking two different tables in PostgreSQL. I do this with a sentence like this:
CREATE TABLE t_aux_prop_delay
AS
SELECT r.cell, r.kpi_name, r.the_geom, d.max as pd_end_point, round(k.avg_samples, 2) AS avg_samples
from t_cell_regions r, kpi_definition d,
(
SELECT cell, kpi_name, avg(kpi_value) AS avg_samples
from t_prop_delay_values
GROUP BY cell, kpi_name
) k
WHERE r.kpi_name = d.kpi_name
AND r.cell = k.cell
AND r.kpi_name = k.kpi_name
ORDER BY r.cell, d.max;
I want to know if it is possible to propagate the indexes from one of those tables to the new table so I do not have to create them all over again.
Maybe this select can help you:
SELECT REPLACE(indexdef, indexname, indexname||1)
FROM pg_indexes
WHERE TABLENAME = 'YOUR_TABLE_NAME';
It will give you a "CREATE INDEX" code with a different name (that is why I have added/concatenated 1 at the end). You are creating a new object so it has to have a different name...
And here is another code where you can also get the "CREATE INDEX" code with the different table name.
SELECT REPLACE(REPLACE(indexdef, indexname, indexname||1), tablename, 'your_tablename')
FROM pg_indexes
WHERE TABLENAME = 'personeel_medspec';
I changed data type of column from nvarchar to datetime keeping the column name same in SQL server. does it requires to drop and recreate the views which depends on that column?
When you change base table column types you should always do at least refresh view:
exec sp_refreshview 'yourView'
And that is why.
When you change your table definition, the view metadata is not refreshed.
Now immagine that you have users that have no permission on the base table, but have SELECT permission on the view. If they ask sp_help about the columns of this view, or if they open Columns folder in OE, it will still show the old types. Then the users can make illegal queries an they'll be going crazy to figure out what happens.
I give you this example to show what can happen.
create view dbo.vw_test_types as
select art_code, art_desc
from dbo.test_types;
go
insert into dbo.test_types (art_code, art_desc)
values ('123', 'trekking shoes');
go
alter table dbo.test_types alter column art_code int;
go
exec sp_help 'dbo.vw_test_types'
select art_desc + ' ' + art_code as full_art_desc
from dbo.vw_test_types;
--Msg 245, Level 16, State 1, Line 33
--Conversion failed when converting the varchar value 'trekking shoes ' to data type int.
exec sp_refreshview 'dbo.vw_test_types'
go
exec sp_help 'dbo.vw_test_types'
go
Here you create a table containing only varchar columns. You then create a view based on this table and enter a row. At this point you decide to change the art_code type to int, the command works fine.
But look at your view's metadata (exec sp_help 'dbo.vw_test_types'), it still shows you only varchar columns.
Now there is a user that has no access to the base table, he wants to show up the whole description including the art_code. He opens SSMS -> OE -> dbo.vw_test_types -> Columns and see that all columns are varchar, so he just concatenates art_desc and art_code. And gets the error! And he can really be perplessed about it, he SEES varchar only but the error told him abot int type
And even worse. Think if the user has built some reports based on that query. One day all these reports do not work at all and they can be configured to NOT show the error to the user, simply do not work showing "an error happen when processing the query"
Not as an answer but to further sepupic's answer above (who beat me to the punch!), consider the following:
Begin Transaction
Create Table dbo.SOTest(col1 Numeric(12,2));
Go
Create View dbo.SOTestV As Select col1 From dbo.SOTest
Go
Select c.name, t.name
From sys.columns As c
Join sys.types As t
On c.user_type_id = t.user_type_id
Where c.object_id = Object_Id('dbo.SoTestV')
Go
Alter Table dbo.SOTest Alter Column col1 Int
Go
Select c.name, t.name
From sys.columns As c
Join sys.types As t
On c.user_type_id = t.user_type_id
Where c.object_id = Object_Id('dbo.SoTestV')
Go
Exec sys.sp_refreshview N'dbo.SOTestV'
Go
Select c.name, t.name
From sys.columns As c
Join sys.types As t
On c.user_type_id = t.user_type_id
Where c.object_id = Object_Id('dbo.SoTestV')
Go
Rollback Transaction
The results of which will be
col1 numeric
(1 row(s) affected)
col1 numeric
(1 row(s) affected)
col1 int
Thus proving that the metadata in the view is still showing the previous datatype until refreshed.
I need to change the computation logic for an existing computed column in SQL Server.
And I want to keep the column name unchanged.
I wrote a script to do this job. And the script would
Drop the computed column
Recreate the computed column with new computation logic with same name.
The issue is this script may run multiple times in the future, however I don't want this script to drop and create the column every time.
Is there some way to avoid this column to be deleted and created more than once?
One way I could think of is to create a dummy check constraint.
So before drop and recreate the computed column, first check if that dummy check constraint exists or not.
If it does not exists, then create the dummy constraint and drop/create the computed column.
Not sure if there is any other easy way to solve this.
you may rely upon the computed_columns system table:
SELECT t.name
,SCHEMA_NAME(t.schema_id) AS [schema_name]
,c.name
,c.definition
FROM sys.computed_columns c
JOIN sys.tables t ON t.object_id = c.object_id;
I think it will require some trial and error to get the correct code but you should be able to build a check script to verify the current formula and trigger the DROP/CREATE only when needed.
possible issue: if an outdated version of the script is run it may revert the formula to a previous version so a timestamp check must be implemented.
If you don't mind using hardcoded value (not good), this script might help.
You will have to use a hard coded column_id or better figure out a way to retrieve it (count columns for this table maybe?).
CREATE TABLE dbo.Table_1(ID int identity(0, 1), V1 int NULL, V2 int NULL, V3 AS V1*V2) ON [PRIMARY]
select top 10 * from sys.computed_columns where OBJECT_ID('table_1') = object_id
If Exists(Select 1 From sys.computed_columns where OBJECT_ID('table_1') = object_id and name = 'v3' and column_id<=
4)
Begin
Print 'Update Computed Column'
Alter TAble Table_1 Drop column V3;
Alter Table Table_1 Add V3 as V1*V2
End
Else
Begin
Print 'No Update'
End
select top 10 * from sys.computed_columns where OBJECT_ID('table_1') = object_id
I am trying to join records in sys.columns for a view, to the records in sys.columns for the table it is referencing, because i need the values of is_nullable, is_computed and default_object_id columns for the columns that are selected in the view.
The sys.columns records for the view have "incorrect" values, which you can observe by running the example queries below:
CREATE TABLE TestTable (
FieldA int NOT NULL,
FieldB int DEFAULT (1),
FieldC as CONVERT(INT, FieldA + FieldB),
FieldD int NOT NULL
)
GO
CREATE VIEW TestView WITH SCHEMABINDING AS
SELECT FieldA, FieldC as TestC, FieldB + FieldC as TestD
FROM dbo.TestTable WHERE FieldD = 1
GO
SELECT OBJECT_NAME(c.object_id) as ViewName, c.name as ColumnName,
c.is_nullable as Nullable, c.is_computed as Computed,
cast(CASE WHEN c.default_object_id > 0 THEN 1 ELSE 0 END as bit) as HasDefault
FROM sys.columns c
WHERE object_id = OBJECT_ID('TestTable')
GO
SELECT OBJECT_NAME(c.object_id) as ViewName, c.name as ColumnName,
c.is_nullable as Nullable, c.is_computed as Computed,
cast(CASE WHEN c.default_object_id > 0 THEN 1 ELSE 0 END as bit) as HasDefault
FROM sys.columns c
WHERE object_id = OBJECT_ID('TestView')
GO
I have tried using system views to join on dependencies, but they do not give us information about which column in the view refers to which column in the table:
-- dm_sql_referenced_entities gives us all columns referenced, but all records
-- have referencing_minor_id 0, so we do not know which column refers to what
SELECT * FROM sys.dm_sql_referenced_entities('dbo.TestView', 'OBJECT')
GO
-- sql_dependencies gives us all columns referenced, but all records has
-- column_id 0 so we can not use this either of joining the columns
SELECT * FROM sys.sql_dependencies WHERE object_id = OBJECT_ID('TestView')
GO
-- sql_expression_dependencies just tells us what table we are referencing
-- if view is not created WITH SCHEMABINDING. If it is, it will return columns,
-- but with referencing_minor_id 0 for all records, so not able use this either
SELECT * FROM sys.sql_expression_dependencies
WHERE referencing_id = OBJECT_ID('TestView')
GO
This unanswered post on social.msdn submitted by someone seems to be the same issue:
http://social.msdn.microsoft.com/Forums/en-US/transactsql/thread/4ae5869f-bf64-4eef-a952-9ac40c932cd4
You said "i need to know that TestView TestC refers to a computed column". This is not supported by SQL Server 2008 R2 (not sure for 2012 though, but i doubt it).
First you can query sys.columns or INFORMATION_SCHEMA.COLUMNS and you won't find what you want.
If you dig deeper, you will most probably try sys.sql_expression_dependencies and sys.dm_sql_referenced_entities (N'dbo.TestView', N'OBJECT'), but you can find table-column mapping there, not column-column. SQL server stores dependency information by 'high level' object (table, trigger...), not by its details (column). You will find same in sys.sysdepends. As a matter of fact dependency information is in SQL server unreliable.
At last, your only possibility would be to parse the view body by yourself. It can be found in sys.sql_modules:
SELECT m.definition
FROM
sys.objects o
JOIN sys.sql_modules m
ON m.object_id = o.object_id
WHERE
o.object_id = object_id('dbo.TestView')
and o.type = 'V'
Parsing T-SQL is VERY hard, it could really push you to the limit of your efforts. For instance, it should be more or less easy to grab table references from the view, and then table columns, especially if your view is schema-bound. But if it's not, well... just think of asterisks that reference OUTER APPLY, which references recursive CTE...
Anyway, good luck!
Currently, when views are created there are two operations that happen at a high level - parsing & binding. Parsing is basically checking for syntax of the statement, keywords & such. Binding is the process of mapping the identifiers (object names, column names) in the statement to the corresponding objects (tables, views, functions, columns etc.) & derivation of types. Additionally, in case of view similar to SELECT statements you can optionally alias column references and expressions in the SELECT list or after the view name (ex: create view v1(a) as select i from t).
After binding, we persist only the column aliases & the derived types in the metadata since a view is logically a table derived from a query expression. So there is currently no way to determine the expression that the column aliases map to or what it contains (columns or functions or literals etc.)
Only way to obtain the information you are looking for is to parse the view definition & perform your own binding. I believe we already have a bug that tracks the feature request to expose more richer dependency information regarding the mapping of aliases to column expressions in view definitions.
Lastly, SQL Server Developer Studio or Visual Studio Database Project uses the managed T-SQL parser to track such references so you can do refactoring or renaming for example using the project.
Hope this helps clarify the problem/current implementation.
It seems like the misunderstanding is the assumption that object_id is a primary key in sys.columns. It is not. The object_id in sys.columns relates to the object_id in sys.objects.
So:
SELECT C.*
FROM sys.objects T
INNER JOIN sys.columns C
ON T.object_id = C.object_id
WHERE T.type in ('S','U') -- System Tables and User Tables
AND T.name = 'Address' -- Table Name
order by C.Column_ID
will return the columns in the "Address" table in AdventureWorks.
I have found that how to determine what columns are primary key column of a given table by using this query:
SELECT CONSTRAINT_NAME, COLUMN_NAME
FROM
INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE
WHERE TABLE_NAME='tablename_here' AND
CONSTRAINT_NAME LIKE 'PK_%'
I can find , what the identity seed and increment is by using this query:
SELECT IDENT_SEED('tablename_here'), IDENT_INCR('tablename_here')
I cant use the constraint information, because a primary key constraint can be across multiple columns. And i cant seem to find any Transact SQL function to give my the identity information.
Can anybody help me to understand how to find the identity information?
I am using SQL Server 2000.
To find the IDENTITY column in a given table you can use this:
SELECT *
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME='tablename_here'
AND COLUMNPROPERTY(OBJECT_ID('tablename_here'),COLUMN_NAME,'IsIdentity') = 1
You can use the COLUMNPROPERTY function to check whether a column uses the identity property.
SELECT sys.tables.name, sys.columns.name
FROM sys.tables
INNER JOIN sys.columns
ON sys.tables.object_id = sys.columns.object_id
WHERE is_identity = 1
AND sys.tables.name = 'MyTable'