I have a simple table:
File: path | deleted | categories | description
I want to use the table for a Linq to SQL entity model. On the model, that column path (which is string there, nvarchar in database) can be set as primary key, but this is not the case with the Visual Studio table designer.
A path is what makes one file unique, so I have to make sure there are no duplicate paths in the table. How to achieve that ? Thanks for the time.
You make a column unique with a UNIQUE constraint, a NOT NULL UNIQUE constraint, or PRIMARY KEY constraint in SQL. But every current dbms that I can think of off the top of my head has one or more restrictions on the length of a columnm that can be constrained that way.
That means you have a fairly show-stopping problem for the most general case. A UNC path on Windows, for example, can be about 32,767 characters long.
Linux systems vary widely. 1024 and 4096 seem common, based on quick Google research.
I think you're going to have to put a unique constraint on only a surrogate key. (You have no idea how much it hurts a database guy to say that.) The problem is that you can enforce uniqueness on the surrogate, but not on the thing it takes the place of. And the thing it takes the place of is the important part.
An ID number won't work in the general case; you could easily end up with {1, /etc/adjtime}, {2, /etc/adjtime}, {3, /etc/adjtime}. You need something that somehow ties the real data to the value of the surrogate key. Something like hashbytes() would "work" in T-SQL; linq has similar functions. (But you can have collisions, like you can with almost all hash functions.)
As Martin already explained, you can create a primary key as long as the value <= 450 characters (or 900 characters, if you are able to constrain the data such that Unicode characters are not allowed). This works fine:
CREATE TABLE dbo.sample1
(
path NVARCHAR(450) NOT NULL PRIMARY KEY,
deleted BIT NOT NULL DEFAULT 0
-- ... other columns ...
);
CREATE TABLE dbo.sample2
(
path VARCHAR(900) NOT NULL PRIMARY KEY,
deleted BIT NOT NULL DEFAULT 0
-- ... other columns ...
);
Whether some visual designer in some undeclared version of Visual Studio allows you to do that, I have no idea, but you don't have to create your tables using some visual designer, do you?
If the path is liable to exceed 900 bytes, you can't make that a key, sorry. You'll have to use an IDENTITY column, some other surrogate, or a hash of the path value to use in the key. Example of a hash that will support up to a path of 4000 characters - which doesn't satisfy all potential use cases, but hopefully you are not needing to exceed 4000:
CREATE TABLE dbo.sample3
(
[path] NVARCHAR(4000) NOT NULL,
pathhash AS CONVERT(VARBINARY(900), HASHBYTES('MD5', path))
PERSISTED PRIMARY KEY
);
INSERT dbo.sample3([path])
SELECT '\\some\share\x' + REPLICATE('x', 900) + '.gif'
UNION ALL SELECT '\\some\share\x' + REPLICATE('x', 900) + '.jpg';
Try to run the insert again.
(And again, you can double the 4000 characters to 8000 characters if you can guarantee that no path will contain Unicode characters; in that case, you can use varchar(8000) instead of nvarchar(4000).)
Related
How to change column type from varchar2(10) to char(10) without losing existing data using oracle developer?
ALTER TABLE TBL_NAME
MODIFY (CRTE CHAR(10));
Will it impact existing data?
It should not impact existing data.
See this SQL Fiddle.
I would reconsider changing varchar2 for char. Char will take up more space on HDD as char will always take N bytes. And if your strings are not execatly 10 characters, all of them char will be pain for searches.
There is really no reason to do this. As this "Ask Tom" answer, explains, the two are stored equivalently.
If you want to ensure that the value has exactly 10 characters, then use a check constraint:
ALTER TABLE TBL_NAME ADD CONSTRAINT CHECK (LEN(CRTE) = 10);
The difference is padding the string in result sets, and that is often better handled on an ad-hoc basis. Trailing spaces can be quite tricky to deal with.
If you really want to change the column type, you can use:
alter table t modify ( x char(10) );
This should be safe with existing data, because you are not reducing the length of the column.
I am having a problem with a check constraint in Oracle SQL Developer with Oracle 11g Express Edition.
I want to check if my postal code which is stored as CHAR just contains numbers and no other signs.
Tried various possibilities but nothing worked...
PLZ VARCHAR(5) NOT NULL
ADD CONSTRAINT TC_PLZ CHECK (PLZ LIKE '[0-9][0-9][0-9][0-9][0-9]')
Thanks and best regards,
Michael
Use regexp_like():
ADD CONSTRAINT TC_PLZ CHECK ( regexp_like(PLZ, '^[0-9]{5}$') )
Oracle -- like most databases -- only supports the ANSI standard wildcards in LIKE. These are % for zero or more characters and _ for exactly one character.
Regular expressions are much more powerful (although generally slower).
This might be a bit more efficient:
ADD CONSTRAINT TC_PLZ CHECK (translate(PLZ, '0123456789', '000000000') = '00000')
although I would go with the regular expression for clarity.
If you want to use regular expression patterns you need to use a regex function in the CHECK constraint. This will work:
create table t23 (
PLZ VARCHAR(5) NOT NULL
, CONSTRAINT TC_PLZ CHECK (regexp_LIKE(plz, '^[0-9]{5}$'))
);
Although there's no need for regex; here's an alternative implementation:
create table t42 (
PLZ NUMBER(5,0) NOT NULL
, CONSTRAINT T42_PLZ CHECK (length(plz)=5)
);
It's good practice to store numeric values using the correct datatype.
Check out this LiveSQL demo.
As #MT0 points out, the numeric implementation is not correct if a valid value can start with a leading zero. My purist data modeller persona states that values which must be a fixed number of digits should start at 10000 (for five digit values). However, I must admit I know of one system that did permit leading zeroes in a "numeric" field; they compounded this by allowing variable level values, which meant code 0300 identified something different from 00300. What larks!
I am working on a question bank and each question will have a numerical serial number. My idea was to use each table's primary key and simply compound them into a new column in a view as required. But obviously, SQL Server will automatically start numbering int's at 1. I would like them to instead be 01, 02, 03...09, 10.
Is this even possible? Or is there another way to accomplish what I am trying to do? I am using SQL Server 2014 Standard. Thanks!
This sounds like a presentation issue. I would suggest adding a computed column to zero pad. Here is one way:
alter table add questionid as (right(cast(10000 + id as varchar(255)), 2))
Then you can have an identity id and a zero padded version for output purposes.
No, that is not possible. What you could do is create a Sql View that returns the id formatted as a varchar(2) and append a 0 to the id if it is less than 10. Alternatively you can use a varchar(2) as the primary identifier but you would have to manually create the id at creation time.
This might be a stupid question. I need to have a Id field in a table. This Id is not a key (primary or whatever). The requirement says that user can input 10 digits in this field from the UI. This Id has no relation as of now with any otehr object in the system. Do you think If one can use nvarchar or bigint for this field. Does bigint would be an overhead for this purpose?
If you will ever perform any mathematical work with this column, then obviously an integral type is to be preferred. Alternatively, if this is a pure, opaque identifier, then a char or varchar column would seem to make more sense. And if leading zeros might be important, then obviously the integral types will not work, e.g. if storing a UK phone number, 01234 567890, you'd want that in a char column.
Edit and for bonus points, if it is an id that should always be ten digits:
CREATE TABLE T (
/* Other columns */
Id char(10),
constraint CK_ID_Valid CHECK (LEN(ID)=10 and not ID like '%[^0-9]%')
)
There are chances that business can ask you to start storing alphanumeric data in this key. I would suggest that you stick with nvarchar or varchar. Even if it remains numeric and not used anywhere in some kind of computation, it is perfectly valid to have it as varchar.
You may use NUMERIC(10,0) type
or if you dont any mathematical operation you can use nvarchar type.
I have a column, say PROD_NUM that contains a 'number' that is left padded with zeros. For example 001004569. They are all nine characters long.
I do not use a numeric type because the normal operation on numbers do not make sense on these "numbers" (For example PROD_NUM * 2 does not make any sense.) And since they are all the same length, the column is defined as a CHAR(9)
CREATE TABLE PRODUCT (
PROD_NUM CHAR(9) NOT NULL
-- ETC.
)
I would like to constrain PROD_NUM so it can only contain nine digits. No spaces, no other characters besides '0' through '9'
REGEXP_LIKE(PROD_NUM, '^[[:digit:]]{9}$')
You already received some nice answers on how to continue on your current path. Please allow me to suggest a different path: use a number(9,0) datatype instead.
Reasons:
You don't need an additional check constraint to confirm it contains a real number.
You are not fooling the optimizer. For example, how many prod_num's are "BETWEEN '000000009' and '000000010'"? Lots of character strings fit in there. Whereas "prod_num between 9 and 10" obviously selects only two numbers. Cardinalities will be better, leading to better execution plans.
You are not fooling future colleagues who have to maintain your code. Naming it "prod_num" will have them automatically assume it contains a number.
Your application can use lpad(to_char(prod_num),9,'0'), preferably exposed in a view.
Regards,
Rob.
(update by MH) The comment thread has a discussion which nicely illustrates the various things to consider about this approach. If this topic is interesting you should read them.
Works in all versions:
TRANSLATE(PROD_NUM,'123456789','000000000') = '000000000'
I think Codebender's regexp will work fine but I suspect it is a bit slow.
You can do (untested)
replace(translate(prod_num,'0123456789','NNNNNNNNNN'),'N',null) is null
Cast it to integer, cast it back to varchar, and check that it equals the original string?
In MSSQL, I might use something like this as the constraint test:
PROD_NUM NOT LIKE '%[^0-9]%'
I'm not an Oracle person, but I don't think they support bracketed character lists.
in MS SQL server I use this command:
alter table add constraint [cc_mytable_myfield] check (cast(myfield as bigint) > 0)
Not sure about performance but if you know the range, the following will work.
Uses a CHECK constraint at the time of creating the DDL.
alter table test add jz2 varchar2(4)
check ( jz2 between 1 and 2000000 );
as will
alter table test add jz2 varchar2(4)
check ( jz2 in (1,2,3) );
this will also work
alter table test add jz2 varchar2(4)
check ( jz2 > 0 );