SQL Server 2008 Unique Column that is Case Sensitive - sql

Is there a way to make a column both UNIQUE and Case Sensitive?
I want to be able to put
abcde
and
ABCDE
in a unique column.

The uniqueness can be enforced with a unique constraint.
Whether or not the unique index is case-sensitive is defined by the server's (or the table's) collation.
You can get the current collation of your database with this query:
SELECT DATABASEPROPERTYEX('AdventureWorks', 'Collation') SQLCollation;
and you should get something like:
SQLCollation
————————————
SQL_Latin1_General_CP1_CI_AS
Here, the "CI_AS" at the end of the collation means: CI = Case Insensitive, AS = Accent sensitive.
This can be changed to whatever you need it to be. If your database and/or table does have a case-sensitive collation, I would expect that the uniqueness of your index will be case-sensitive as well, e.g. your abcdef and ABCDEF should be both acceptable as unique strings.
Marc
UPDATE:
I just tried this (SQL Server 2008 Developer Edition x64) - works for me (my database is generally using the "Latin1_General_CI_AS collation, but I can define a different one per table / per VARCHAR column even):
CREATE TABLE TestUnique
(string VARCHAR(50) COLLATE SQL_Latin1_General_Cp1_CS_AS)
CREATE UNIQUE INDEX UIX_Test ON dbo.TestUnique(string)
INSERT INTO dbo.TestUnique(string) VALUES ('abc')
INSERT INTO dbo.TestUnique(string) VALUES ('ABC')
SELECT * FROM dbo.TestUnique
and I get back:
string
ABC
abc
and no error about the unique index being violated.

In case some one needs to do it on an existing table which already has a unique key/index defined on a varchar/nvarchar column, here is the script.
ALTER TABLE [YourTable] DROP CONSTRAINT [UIX_YourUniqueIndex]
GO
ALTER TABLE [YourTable] ALTER COLUMN [YourColumn] [nvarchar](50) COLLATE Latin1_General_CS_AS NOT NULL;
GO
ALTER TABLE [YourTable] ADD CONSTRAINT [UIX_YourUniqueIndex] UNIQUE NONCLUSTERED
(
[YourColumn] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO

I needed to import data from a case sensitive database. When I tried to put the primary key on the column that is the primary key on the source I couldn't do it because of duplicate keys. I changed the collation for the column (varchar) to case sensitive (Right click on the table, choose Design, highlight the column you want to change and click on the elipsis in Collation) and now it works fine. (SQL Server 2008 R2 64 bit).

Thanks #Devraj Gadhavi for the step by step as this is exactly what I needed to do as well. I was about to make those scripts but then (using SSMS 2008R2), I achieved the same in a more lazy :-) way. In the tree view I located my table and column and then right clicked on the column I wanted to change the collation on and chose 'Modify'. In the displayed window, I changed the collation in the properties to the case sensitive one, then anywhere in the open space at the top section of the window (where the columns are listed in table form) I right clicked and chose "Generate Change Script..."

Related

Postgres Import from different table

I'm still fairly new to postgres. I have a table named: university_table with fields: name,
nationality, abbreviation, adjective, person.
I found this sql query to insert data from: https://stackoverflow.com/a/21759321/9469766
Snippet of query below.
How can alter the query to insert these values into my university_country table
-- Create and load Nationality Table - English
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Nationality]') AND type in (N'U'))
DROP TABLE [dbo].[Nationality]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-------------------------------------------------------------------
-- TABLE: [dbo].[Nationality]
-- Creation Date: 02/12/2014
-- Created by: Dan Flynn, Sr. DBA
--
-------------------------------------------------------------------
CREATE TABLE [dbo].[Nationality]
(
[NationalityID] [int] IDENTITY(1,1) NOT NULL,
[Country] [nvarchar](50) NULL,
[Abbreviation] [nvarchar](5) NULL,
[Adjective] [nvarchar] (130) NULL,
[Person] [nvarchar] (60) NULL
) ON [PRIMARY]
GO
-------------------------------------------------------------------------------
-- INSERT VALUES
-------------------------------------------------------------------------------
INSERT INTO [dbo].[Nationality](Country, Abbreviation, Adjective, Person )
VALUES ( 'AMERICAN - USA','US','US (used attributively only, as in US aggression but not He is US)','a US citizen' ),
( 'ARGENTINA','AR','Argentinian','an Argentinian' ),
( 'AUSTRALIA','AU','Australian','an Australian' ),
( 'BAHAMAS','BS','Bahamian','a Bahamian' ),
( 'BELGIUM','BE','Belgian','a Belgian' ),
GO
-------------------------------------------------------------------------------
-- ADD CLUSTERED INDEX
-------------------------------------------------------------------------------
CREATE CLUSTERED INDEX [idxNationality] ON [dbo].[Nationality]
(
[NationalityID] ASC,
[Country] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
EXEC sys.sp_addextendedproperty #name=N'TableDiscription', #value=N'CreatedBy: Dan Flynn, Sr. SQL Server DBA
CreationDate: 02/12/2014
Nationality table contains five columns, i.e.:
1. NationalityID, 2. Country, 3. Abbreviation, 4. Adjective, 5. Person
IDs 1 to 34 are alphabetical countries that are statistically the most popular as far as interaction with the United States. IDs 35 to 248 are also alphabetical for the rest of the countries.
' , #level0type=N'SCHEMA',#level0name=N'dbo', #level1type=N'TABLE',#level1name=N'Nationality'
GO
To convert T-SQL to be compatible with Postres' SQL dialect you can use the following steps.
Remove all square brackets (they are illegal in SQL identifiers). If you have identifiers that require them use double quotes " but I would highly recommend to avoid quoted identifiers completely (so never use " in SQL)
Remove all GO statements and end the statements with ; (Something that is recommended for SQL Server as well)
Remove the [dbo]. schema prefix if you didn't create one in Postgres (you typically don't)
Remove the ON [Primary] option it's not needed in Postgres (the equivalent would be to define a tablespace, but that's hardly ever needed in Postgres)
There is no IF in SQL (or Postgres), to conditionally drop a table use DROP TABLE IF EXISTS ....
There are no clustered indexes in Postgres, so just make that a regular index and remove all the options that are introduced by the WITH keyword.
Comments on tables are defined through comment on, not by calling a stored procedure
identity(x,y) needs to be replaced with the standard SQL generated always as identity
There is no nvarchar type, just make everything varchar and make sure your database was created with an encoding that can store multi-byte characters (by default it's UTF-8, so that should be fine)
Not required, but: it's highly recommended to use snake_case identifiers, rather than CamelCase in Postgres
Putting that all together the script should be something like this:
DROP TABLE IF EXISTS Nationality CASCADE;
CREATE TABLE nationality
(
Nationality_id int generated always as IDENTITY NOT NULL,
Country varchar(50) NULL,
Abbreviation varchar(5) NULL,
Adjective varchar (130) NULL,
Person varchar (60) NULL
);
INSERT INTO Nationality (Country, Abbreviation, Adjective, Person )
VALUES ( 'AMERICAN - USA','US','US (used attributively only, as in US aggression but not He is US)','a US citizen' ),
( 'ARGENTINA','AR','Argentinian','an Argentinian' ),
( 'AUSTRALIA','AU','Australian','an Australian' ),
( 'BAHAMAS','BS','Bahamian','a Bahamian' ),
( 'BELGIUM','BE','Belgian','a Belgian' );
CREATE INDEX idx_Nationality ON Nationality
(
Nationality_ID ASC,
Country ASC
);
comment on table nationality is 'CreatedBy: Dan Flynn, Sr. SQL Server DBA
CreationDate: 02/12/2014
Nationality table contains five columns, i.e.:
1. NationalityID, 2. Country, 3. Abbreviation, 4. Adjective, 5. Person
IDs 1 to 34 are alphabetical countries that are statistically the most popular as far as interaction with the United States. IDs 35 to 248 are also alphabetical for the rest of the countries.
';
I am a bit surprised that there is no primary key defined. You probably want to add:
alter table nationality
add primary key (nationality_id);
There is an SQL standard, but nobody implements it fully. Some database systems like PostgreSQL are better at sticking to the standard, others like Microsoft SQL Server are not.
The upshot of this is that you cannot take SQL that works on one RDBMS and use it with another one. You will have to translate that to the PostgreSQL dialect.

Unique constraint for varbinary column

I have a varbinary column in SQL, and I want this column to be always unique. However, I see that SQL doesn't allow to create a unique constraint on a varbinary column.
Is there any workaround to ensure this uniqueness? Maybe by using some other type of constraint, or something else?
Thanks!
If the varbinary is reasonably short then you could create a computed column of the hex representation and put a unique constraint on that. Ref SQL Server converting varbinary to string for how to convert varbinary to hex string.
Edit1: As pointed out by #GiorgiNakeuri the limit for unique constraints is 900 bytes, so 450 bytes in hex.
Edit2: Alternatively, if you can tollerate a (very) small risk of failure then you could create a computed column with an MD5 hash of the varbinary value and put the unique constraint on that. See the docs for HASHBYTES.
I guess you have VARBINARY(MAX). The length of it is 2^31-1 bytes, but the maximum length of key should be 900 bytes. So you are actually limited with VARBINARY(900). If the size of VARBINARY <= 900, you can add unique index.
As a workaround you can add Trigger and rollback inserts, if there is already same values in table.
A simpler solution could be to manually maintain the uniqueness of the column by checking the existence of the value to be inserted or updated before inserting or updating the column.
Example:
DECLARE #Exists BIT = (
SELECT CASE WHEN COUNT(*) > 0 THEN 1 ELSE 0 END
FROM [Schema].[Table]
WHERE [DecryptedColumn] = #NewValueToCheck
)
IF (#Exists = 0)
BEGIN
-- Insert or Update
END
ELSE
-- Return Error
BEGIN
END
You could make the column a primary key. Scripted test table shown below
/****** Object: Table [dbo].[Table_1] Script Date: 02/03/2015 12:19:22 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[Table_1](
[test] [varbinary](50) NOT NULL,
[rf] [nchar](10) NULL,
CONSTRAINT [PK_Table_1] PRIMARY KEY CLUSTERED
(
[test] 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

Creating a partitioned view in SQL Server 2008 R2 Enterprise

I'm extending some legacy software that splits data up in to multiple schemas by company, for example CP1.ACCOUNTS, CP2.ACCOUNTS, CPN.ACCOUNTS. I'm attempting to create an updatable view of these tables using partitioning, but I'm getting the typical "not updatable because a partitioning column was not found" error. The column I'm trying to partition on is the primary key, and as far as I can tell, isn't any of the things it isn't allowed to be.
So, with table definitions like so:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [CP1].[ACCOUNTS](
[ACCOUNTID] [char](10) NOT NULL,
[LASTNAME] [varchar](60) NOT NULL,
[FIRSTNAME] [varchar](35) NOT NULL,
[MIDDLE] [varchar](26) NULL,
[SUFFIX] [varchar](10) NULL,
[ADDRESS1] [varchar](55) NULL,
[ADDRESS2] [varchar](55) NULL,
[SOME_FLAG] [tinyint] NULL,
CONSTRAINT [ARM_CODE_KEY] PRIMARY KEY CLUSTERED
(
[CODE_] 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 [CP1].[ACCOUNTS] WITH CHECK ADD CONSTRAINT [CK__ACCOUNTS__CODE___4DD705FF] CHECK ((left([ACCOUNTID],(3))='CP1'))
GO
ALTER TABLE [CP1].[ACCOUNTS] CHECK CONSTRAINT [CK__ACCOUNTS__CODE___4DD705FF]
GO
ALTER TABLE [CP1].[ACCOUNTS] ADD DEFAULT ((0)) FOR [SOME_FLAG]
GO
and the rest of the tables defined exactly as above, following the CP2, CP3, CPN pattern, and the view definition being a simple:
CREATE VIEW [ALL].[ACCOUNTS] AS
SELECT * FROM CP1.ACCOUNTS
UNION ALL
SELECT * FROM CP2.ACCOUNTS
--UNION ALL etc...
Inserts would be like:
INSERT INTO [ALL].[ACCOUNTS]
([ACCOUNTID]
,[LASTNAME]
,[FIRSTNAME]
,[MIDDLE]
,[SUFFIX]
,[ADDRESS1]
,[ADDRESS2]
,[SOME_FLAG])
VALUES
('CP1XYZ0001',
'SMITH',
'JOHN',
'Q',
'',
'123 Fake St',
'Apt 2',
0,
GO
generates an error like:
Msg 4436, level 16, State 12, Line 1
UNION ALL view 'ALL.ACCOUNTS' is not updatable because a partitioning column was not found.
Am I missing something simple? Am I just way out in left field here?
You need a constraint that defines which column is used as a partitioning column. As the error suggests, you don't have one defined. As described in the documentation:
To perform updates on a partitioned view, the partitioning column must
be a part of the primary key of the base table. If a view is not
updatable, you can create an INSTEAD OF trigger on the view that
allows updates. You should design error handling into the trigger to
make sure that no duplicate rows are inserted. For an example of an
INSTEAD OF trigger designed on a view, see Designing INSTEAD OF
Triggers.
In other words, SQL Server needs to be able to figure out which table gets the update.
You might be able to alter the tables to contain a company name column, which is then used as part of the primary key. Something like this might work:
create table . . .
CompanyName as 'CompanyA',
primary key (AccountId, CompanyName)
. . .
The alternative is to use an instead of trigger, as suggested in the documentation.
In case someone comes upon this, you can use a computed column for partitioning, just make sure to make it a persisted computed column.
In this case, the computed column should be left([ACCOUNTID],(3) and the partition constraint would be <computed column> = 'CP1'. Note: using left() in the constraint will cause it to still scan all partitions. The CHECK constraints can only use these operators: BETWEEN, AND, OR, <, <=, >, >=, =.
Also, since the question referenced enterprise edition, you'd get better performance using a partitioned table instead of a partitioned view.

Adding a uniqueidentifier column and adding the default to generate new guid

I have the following SQL command:
ALTER TABLE dbo.UserProfiles
ADD ChatId UniqueIdentifier NOT NULL,
UNIQUE(ChatId),
CONSTRAINT "ChatId_default" SET DEFAULT newid()
I want to be able to make this column unique, and I want it to be able to generate a new guid every time a row is added to the table. This column is not an IDENTITY column because I already have one. This is something separate. How would I go about adding this column to a table with users already in it.
see this sample:
create table test (mycol UniqueIdentifier NOT NULL default newid(), name varchar(100))
insert into test (name) values ('Roger Medeiros')
select * from test
for add a not null field on a populated table you need this.
alter table test add mycol2 UniqueIdentifier NOT NULL default newid() with values
CREATE UNIQUE NONCLUSTERED INDEX IX_test ON dbo.test
(
mycol
) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
Don't use newid() as default, instead use newsequentialid(). newid() creates a lot of fragmentation and that's bad for indexes.
As far as adding the new column to a table with existing data, simply do this:
ALTER TABLE your_table
ADD your_column UNIQUEIDENTIFIER DEFAULT newsequentialid() NOT null

Access DateTime of row written to table if no DateTime field in table - SQL Server 2008

I have a situation where I need to find out the most recent entry written to a table but the person who developed the database created the table without a DateTime field or an auto-incrementing ID field.
I am wondering is there any way of accessing some built in DateTime proprty recorded by SQL Server or any other way of determining the more recent entry out of say two results like this:
idp_fund_id IDPID
14 1653
18 1653
Below is the structure
CREATE TABLE [dbo].[web_IDP_Lifestyle](
[idp_fund_id] [int] NOT NULL,
[IDPID] [int] NOT NULL,
CONSTRAINT [PK_web_IDP_Lifestyle] PRIMARY KEY CLUSTERED
(
[idp_fund_id] ASC,
[IDPID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
In short: no.
You might be able to compare for existence of those values in backups... depending on how far the backups go.
I'm assuming that the idp_fund_id of 18 might have been written before the value of 14?
Nope.
SQL Server doesn't keep detailed records on this. If it DID, think about how large the metadata would have to be in order to track the update/insert time for each and every record in each and every table.
SQL doesn't order inserts by default, and I don't think you can check what page it was inserted on to determine this with any reliability, either.
If you want to track something like this, you need to create a field/table for it to track it yourself and implement some triggers.