When you create a table via scripts it appears to just list the columns in order of creation. It's a minor annoyance, but I like a certain method to the madness. When I later add a column with ALTER TABLE, how do I make it show up in a specific place when viewing the table in interactive tools such as enterprise manager?
ex:
Table Foo
---------
FooID
BarID
Name
Address
Worth
I want to
ALTER TABLE Foo
ADD BazID INT NULL
and have BazID listed between BarID and Name when I use Management Studio.
You can't do that - a SQL Server table really doesn't know anything about order of columns - there is no order in the tuple algebra that's the foundation of the relational database systems.
What SQL Server Management Studio shows you is just its own display "optimization" - you can change it there, but the standard SQL Data Definition Language (DDL) statement have no notion of "column order" - you cannot ALTER a table to move a column to a different position.
If you change the column display order in SSMS, what it'll do in the background is rename the old table to a temporary name, create the new table with the columns in the order you specified, and then copy around the data. That's why modifying the column order on a large table can take almost forever - it's not just a simple little DDL statement to be executed - it's a major undertaking.
Marc
The way the sql server management studio and other big name tools like redgate do it, is to make a new temp table, copy the information over, then drop the old table (constraints & unique indexes first), rename the temp table to the old table name, then re-add any constraints and indexes. You really can't re-order what's already there.
Here's an example:
-- here's a sales table
CREATE TABLE [dbo].[Sales](
[SalesId] [int] IDENTITY(1,1) NOT NULL,
[Month] [int] NOT NULL,
[Year] [int] NOT NULL,
[InvoiceAmount] [decimal](15, 2) NOT NULL,
[SalesRepId] [int] NOT NULL,
[BranchId] [int] NOT NULL,
CONSTRAINT [PK_Sales] PRIMARY KEY CLUSTERED
(
[SalesId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
--Here's the sales table adding a column called description between SalesId & Month
BEGIN TRANSACTION
SET QUOTED_IDENTIFIER ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
COMMIT
BEGIN TRANSACTION
GO
CREATE TABLE dbo.Tmp_Sales
(
SalesId int NOT NULL IDENTITY (1, 1),
Description varchar(MAX) NULL,
Month int NOT NULL,
Year int NOT NULL,
InvoiceAmount decimal(15, 2) NOT NULL,
SalesRepId int NOT NULL,
BranchId int NOT NULL
) ON [PRIMARY]
TEXTIMAGE_ON [PRIMARY]
GO
SET IDENTITY_INSERT dbo.Tmp_Sales ON
GO
IF EXISTS(SELECT * FROM dbo.Sales)
EXEC('INSERT INTO dbo.Tmp_Sales (SalesId, Month, Year, InvoiceAmount, SalesRepId, BranchId)
SELECT SalesId, Month, Year, InvoiceAmount, SalesRepId, BranchId FROM dbo.Sales WITH (HOLDLOCK TABLOCKX)')
GO
SET IDENTITY_INSERT dbo.Tmp_Sales OFF
GO
DROP TABLE dbo.Sales
GO
EXECUTE sp_rename N'dbo.Tmp_Sales', N'Sales', 'OBJECT'
GO
ALTER TABLE dbo.Sales ADD CONSTRAINT
PK_Sales PRIMARY KEY CLUSTERED
(
SalesId
) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
COMMIT
Related
I'd like to create the new table only if it does not already exist in the database. So I use the following:
IF (NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'dbo'
AND TABLE_NAME = 'FactSend'))
BEGIN
SET ANSI_NULLS ON;
SET QUOTED_IDENTIFIER ON;
CREATE TABLE [MyDB].[dbo].[FactSend](
[Id] [varchar](100) NOT NULL,
[FlowId] [int] NULL,
[Name] [nvarchar](550) NULL,
[Channel] [varchar](100) NOT NULL,
[Date] [datetime] NULL,
CONSTRAINT [PK_FactSend] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
ALTER TABLE [MyDB].[dbo].[FactSend] WITH CHECK ADD CONSTRAINT [FK_FactSend_DimFlow] FOREIGN KEY([FlowId])
REFERENCES [MyDB].[dbo].[DimFlow] ([Id])
ALTER TABLE [MyDB].[dbo].[FactSend] CHECK CONSTRAINT [FK_FactSend_DimFlow]
END
But I get the following error:
There is already an object named 'FactSend' in the database.
I know there is, that is why I put that in an IF so that the CREATE is skipped.
Too long for a comment but a wild guess. The database you're connected to isn't MyDB and so you're checking in a different database for the existence of FactSend; and then trying to create it in MyDB. Does the following work?
USE MyDB;
GO
IF (NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'dbo'
AND TABLE_NAME = 'FactSend'))
BEGIN
SET ANSI_NULLS ON;
SET QUOTED_IDENTIFIER ON;
CREATE TABLE [dbo].[FactSend](
[Id] [varchar](100) NOT NULL,
[FlowId] [int] NULL,
[Name] [nvarchar](550) NULL,
[Channel] [varchar](100) NOT NULL,
[Date] [datetime] NULL,
CONSTRAINT [PK_FactSend] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
ALTER TABLE [dbo].[FactSend] WITH CHECK ADD CONSTRAINT [FK_FactSend_DimFlow] FOREIGN KEY([FlowId])
REFERENCES [dbo].[DimFlow] ([Id])
ALTER TABLE [dbo].[FactSend] CHECK CONSTRAINT [FK_FactSend_DimFlow]
END
When referencing an object with 2 part naming (i.e. dbo.MyTable, sys.columns, INFORMATION_SCHEMA.TABLES), the database you are currently connected to will be used. Writing a query/statement with a 3 part naming convention does not change the context of the database you are using (just like using 4 part naming convention doesn't change the server you are connected to).
I suspect that you were connected to the default database; probably master. As a result your EXISTS checked in the database master for the table dbo.FactSend.
In effect, your query was more like the below:
USE master;
IF (NOT EXISTS(SELECT * FROM master.INFORMATION_SCHEMA.TABLES --technically master isn't needed here, it's just to show the point
WHERE TABLE_SCHEMA = 'dbo'
AND TABLE_NAME = 'FactSend'))
BEGIN
SET ANSI_NULLS ON;
SET QUOTED_IDENTIFIER ON;
CREATE TABLE [MyDB].[dbo].[FactSend](
[Id] [varchar](100) NOT NULL,
[FlowId] [int] NULL,
...
So, to confirm, you were checking to the existence of the object master.dbo.FactSend and then, if that didn't exist, creating the object MyDB.dbo.FactSend. Of course, that means that no matter how many times you do(try to) create the MyDB.dbo.FactSend, it'll never mean the object master.dbo.FactSend exists; so the NOT EXISTS will always evaluate to true.
Making sure you are connected to the right database is really important. Personally, when using scripts to create objects I recommend against using 3 part naming. Instead declare your database prior (using USE), and then create your objects using 2 part naming. That way you always know the context of the database the objects are being created in, can't "accidental" create them in the wrong one, and if you need to change the database (maybe you're scripting them to a different database) you only need to change the USE statement and not every reference. Of course, if you are referring to objects in other databases then you'd have to use 3 part naming, but I'm specifically talking about when everything in tidily in one DB.
I have a SQL Server 2012 Web Edition within the AWS RDS database service. The collation is the default one: SQL_Latin1_General_CP1_CI_AS.
In one of the tables I have a column MODIFIED_BY of type varchar(128) - the column collation is default.
The usual values stored into this column are GUIDS with the exception of a few which are hard coded to System. As you can tell it's a table for storing the audit trail transactions where we store the id or System if modification was done as part of the migration.
Anyways - everything above was just the context. Now the weird problem:
I get no records if I run the following, although I should have:
select *
from AUDIT_LOG_TRANSACTIONS alt
where lower(ltrim(rtrim(alt.MODIFIED_BY)))='system'
or
select *
from AUDIT_LOG_TRANSACTIONS alt
where lower(ltrim(rtrim(alt.MODIFIED_BY)))=convert(varchar,'system')
or
select *
from AUDIT_LOG_TRANSACTIONS alt
where lower(ltrim(rtrim(alt.MODIFIED_BY)))=convert(varchar(128),'system')
I get records if I run the following:
select *
from AUDIT_LOG_TRANSACTIONS alt
where alt.MODIFIED_BY like '%System%'
or
select *
from AUDIT_LOG_TRANSACTIONS alt
where lower(ltrim(rtrim(CAST(alt.MODIFIED_BY AS nvarchar(max)))))='system'
UPDATE
Here's the table create script:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[AUDIT_LOG_TRANSACTIONS](
[AUDIT_LOG_TRANSACTION_ID] [int] IDENTITY(1,1) NOT NULL,
[DATABASE] [nvarchar](128) NOT NULL,
[TABLE_NAME] [nvarchar](261) NOT NULL,
[TABLE_SCHEMA] [nvarchar](261) NOT NULL,
[AUDIT_ACTION_ID] [tinyint] NOT NULL,
[HOST_NAME] [varchar](128) NOT NULL,
[APP_NAME] [varchar](128) NOT NULL,
[MODIFIED_BY] [varchar](128) NOT NULL,
[MODIFIED_DATE] [datetime] NOT NULL,
[AFFECTED_ROWS] [int] NOT NULL,
[SYSOBJ_ID] AS (object_id([TABLE_NAME])),
PRIMARY KEY CLUSTERED
(
[AUDIT_LOG_TRANSACTION_ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[AUDIT_LOG_TRANSACTIONS] ADD DEFAULT (db_name()) FOR [DATABASE]
GO
ALTER TABLE [dbo].[AUDIT_LOG_TRANSACTIONS] ADD CONSTRAINT [DF_AUDIT_LOG_TRANSACTIONS_MODIFIED_BY] DEFAULT ('System') FOR [MODIFIED_BY]
GO
Could it be there are some "hard to see" characters in your string ?
To eliminate this possibility, I would run a query like this:
select alt.*, LEN(alt.MODIFIED_BY) as 'Length', cast(alt.MODIFIED_BY as varbinary(max)) as 'Bytes' from AUDIT_LOG_TRANSACTIONS alt where alt.MODIFIED_BY like '%System%'
Check that the Length and Bytes columns are what you expect to see.
Using SQL Server Management Studio, my issue stems from a database creation script. The script is written to create a database, many of whose tables have an identity column:
CREATE TABLE Workshop
(
WorkshopID int IDENTITY,
WorkshopName varchar(40) NOT NULL,
Description varchar(800),
CONSTRAINT PK_Workshop PRIMARY KEY (WorkshopID)
);
My issue is that even with the script plainly creating a column as an identity column, after the script runs none of the columns that should be identity columns actually have that column set to be identity.
To clarify: Running the above code will create that table as specified except WorkshopID will not be an identity column.
What needs to change so that the script will work as written?
FYI, if you generate script for this using SQL Management Studio's designer, this is the resulting script:
/* To prevent any potential data loss issues, you should review this script in detail before running it outside the context of the database designer.*/
BEGIN TRANSACTION
SET QUOTED_IDENTIFIER ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
COMMIT
BEGIN TRANSACTION
GO
CREATE TABLE dbo.Table_1
(
WorkshopID int NOT NULL IDENTITY (1, 1),
WorkshopName varchar(40) NOT NULL,
Description varchar(800) NULL
) ON [PRIMARY]
GO
ALTER TABLE dbo.Table_1 ADD CONSTRAINT
PK_Table_1 PRIMARY KEY CLUSTERED
(
WorkshopID
) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
ALTER TABLE dbo.Table_1 SET (LOCK_ESCALATION = TABLE)
GO
COMMIT
If you create the table and then script it using the Create To... menu option you get a completely different script:
USE [MyDatabase]
GO
/****** Object: Table [dbo].[Workshop] Script Date: 11/27/2012 14:05:33 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[Workshop](
[WorkshopID] [int] IDENTITY(1,1) NOT NULL,
[WorkshopName] [varchar](40) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[Description] [varchar](800) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
CONSTRAINT [PK_Workshop] PRIMARY KEY CLUSTERED
(
[WorkshopID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
I have a table whose primary key is also a foreign key to the primary key of another table (i.e. "inheritance" as simulated in a database).
/****** Object: Table [dbo].[BaseClass] Script Date: 07/15/2011 18:17:27 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[BaseClass](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Title] [nvarchar](50) NOT NULL,
[Description] [nvarchar](max) NOT NULL,
CONSTRAINT [PK_BaseClass] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
USE [TestConcepts]
GO
/****** Object: Table [dbo].[DerivedTable] Script Date: 07/15/2011 18:17:49 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[DerivedTable](
[ID] [int] IDENTITY(1,1) NOT NULL,
[SpecialProperty] [uniqueidentifier] NOT NULL,
CONSTRAINT [PK_DerivedTable] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[DerivedTable] WITH CHECK ADD CONSTRAINT [FK_DerivedTable_BaseClass] FOREIGN KEY([ID])
REFERENCES [dbo].[BaseClass] ([ID])
GO
ALTER TABLE [dbo].[DerivedTable] CHECK CONSTRAINT [FK_DerivedTable_BaseClass]
GO
What is the proper way to insert records in this situation? Obviously an insert doesn't return the PK of the inserted row (plus the child's table's PK is identity as well).
Here are a few examples of this pattern.
The sub-type table should not have auto-increment ID, the ID matches the one in the super-type table.
The basic technique (using your example) looks something like
DECLARE #MY_ID integer;
INSERT INTO BaseTable(Title, Description)
VALUES ('title_here', 'blah, blah');
SELECT #MY_ID = SCOPE_IDENTITY();
INSERT INTO DerivedTable(ID, SpecialProperty)
VALUES (#MY_ID, newid()); -- the SpecialProperty is uniqueidentifier
One approach is to create a view, one for each sub-type table, or just one over all of them. Then create an INSTEAD OF INSERT TRIGGER on the view and use the technique inside the trigger.
You may also find this technique for capturing multiple inserted IDs useful too.
Nobody calls this "inheritance". That's not what it is. It's a relation, the R in RDBMS.
INSERTs do tell you the PK of the just-inserted row. Look at ##SCOPE_IDENTITY on SQL Server.
The DERIVEDCLASS table could have an auto-incremented (identity) PK but if so there must be another column that is a foreign key reference back to BASETABLE:
BASETABLE
id int pk autoincrement
baseattribute1
baseattribute2
etc etc
DERIVEDTABLE
id int pk autoincrement
**baseid** foreign key references BASETABLE(id)
extendedattribute1
extendedattribute2
This would permit multiple derivations of each base entity. Placing a unique index on DERIVEDTABLE.baseid or making baseid the PK would prevent this, if that is desired.
The following would instantiate the members of the base class and their derived instances and/or extended properties if any [which it is will depend on whether baseid has unique constraint in DerivedTable; if the latter it could be the PK in a one-to-one relationship with BaseTable, rather than a many-to-one]:
select * from BASETABLE
LEFT JOIN DERIVEDTABLE
on BASETABLE.id = DERIVEDTABLE.baseid
Instances of the base class that have not been extended will have NULL in the extendedattribute columns.
To find only those entities that have been extended use an inner join:
select * from DERIVEDTABLE
inner join BASETABLE
on DERIVEDTABLE.baseid = BASETABLE.id
So I have a table in SQL Server w/ a primary key column, and 4 other columns. When I modify the table, and select the primary key column to be identity, it won't let me save the table.
How can I make it an identity column through T-SQL or something without going to the UI?
Thanks.
Here's the create
USE [db]
GO
/****** Object: Table [dbo].[tblMessages] Script Date: 04/05/2011 11:58:25 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[tblMessages](
[messageId] [int] NOT NULL,
[messageText] [varchar](500) NOT NULL,
[messageLatitude] [float] NOT NULL,
[messageLongitude] [float] NOT NULL,
[messageTimestamp] [datetime] NOT NULL,
PRIMARY KEY CLUSTERED
(
[messageId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
You cannot turn an existing column into an IDENTITY column after it's been created.
ALTER TABLE dbo.YourTable
ALTER COLUMN YourColumn INT IDENTITY
will cause an error:
Msg 156, Level 15, State 1, Line 2
Incorrect syntax near the keyword
'IDENTITY'.
You need to create a new column of type INT IDENTITY and then possibly drop the old one. Or if your table is still empty: drop it and re-create it with the correct settings for your ID column
ALTER TABLE MyTable
ADD NewIdentity INT IDENTITY;
ALTER TABLE MyTable
DROP COLUMN OldPK;
EDIT
If your table is empty, just drop it and add IDENTITY after INT on your PK column and be done with it.