I am working on a project where I have to implement some new functionality.
In the process I have to design some tables and build some editors for that data.
I have one table for categories and one for types.
On client side I have to build some lists using those types, but each list must use types from only one category.
I don't like the idea to use PKs in my C#. I would rather create a column name "Code" in the category table and use that in my C# code, when preparing the lists.
EDIT: I do not mean removing the PK (I will have an int base Id column). I mean adding another column to the category ("Code") to use jsut in C# as string constants, instead of ids.
Is this an okay idea?
If I've understood your question, I would recommend the following structure for your tables:
Table Category
ID Int -- primary key
Code Varchar(8) -- Code value displayed to users
Description Varchar(100)
Table Item
ID Int -- primary key
CategoryID Int -- foreign key to Category
Code Varchar(8) -- Code value displayed to users
Description Varchar(100)
This way, if you change a Category record's Code, nothing changes behind the scenes, and the key values are never exposed to your users.
Can I make a primary key like 'c0001, c0002' and for supplier 's0001, s0002' in one table?
The idea in database design, is to keep each data element separate. And each element has its own datatype, constraints and rules. That c0002 is not one field, but two. Same with XXXnnn or whatever. It is incorrect , and it will severely limit your ability to use the data, and use database features and facilities.
Break it up into two discrete data items:
column_1 CHAR(1)
column_2 INTEGER
Then set AUTOINCREMENT on column_2
And yes, your Primary Key can be (column_1, column_2), so you have not lost whatever meaning c0002 has for you.
Never place suppliers and customers (whatever "c" and "s" means) in the same table. If you do that, you will not have a database table, you will have a flat file. And various problems and limitations consequent to that.
That means, Normalise the data. You will end up with:
one table for Person or Organisation containing the common data (Name, Address...)
one table for Customer containing customer-specific data (CreditLimit...)
one table for Supplier containing supplier-specific data (PaymentTerms...)
no ambiguous or optional columns, therefore no Nulls
no limitations on use or SQL functions
.
And when you need to add columns, you do it only where it is required, without affecting all the other sues of the flat file. The scope of effect is limited to the scope of change.
My approach would be:
create an ID INT IDENTITY column and use that as your primary key (it's unique, narrow, static - perfect)
if you really need an ID with a letter or something, create a computed column based on that ID INT IDENTITY
Try something like this:
CREATE TABLE dbo.Demo(ID INT IDENTITY PRIMARY KEY,
IDwithChar AS 'C' + RIGHT('000000' + CAST(ID AS VARCHAR(10)), 6) PERSISTED
)
This table would contain ID values from 1, 2, 3, 4........ and the IDwithChar would be something like C000001, C000002, ....., C000042 and so forth.
With this, you have the best of both worlds:
a proper, perfectly suited primary key (and clustering key) on your table, ideally suited to be referenced from other tables
your character-based ID, properly defined, computed, always up to date.....
Yes, Actually these are two different questions,
1. Can we use varchar column as an auto increment column with unique values like roll numbers in a class
ANS: Yes, You can get it right by using below piece of code without specifying the value of ID and P_ID,
CREATE TABLE dbo.TestDemo
(ID INT IDENTITY(786,1) NOT NULL PRIMARY KEY CLUSTERED,
P_ID AS 'LFQ' + RIGHT('00000' + CAST(ID AS VARCHAR(5)), 5) PERSISTED,
Name varchar(50),
PhoneNumber varchar(50)
)
Two different increments in the same column,
ANS: No, you can't use this in one table.
I prefer artificial primary keys. Your requirements can also be implemented as unique index on a computed column:
CREATE TABLE [dbo].[AutoInc](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Range] [varchar](50) NOT NULL,
[Descriptor] AS ([range]+CONVERT([varchar],[id],(0))) PERSISTED,
CONSTRAINT [PK_AutoInc] PRIMARY KEY ([ID] ASC)
)
GO
CREATE UNIQUE INDEX [UK_AutoInc] ON [dbo].[AutoInc]
(
[Descriptor] ASC
)
GO
Assigning domain meaning to the primary key is a practice that goes way, way back to the time when Cobol programmers and dinosaurs walked the earth together. The practice survives to this day most often in legacy inventory systems. It is mainly a way of eliminating one or more columns of data and embedding the data from the eliminated column(s) in the PK value.
If you want to store customer and supplier in the same table, just do it, and use an autoincrementing integer PK and add a column called ContactType or something similar, which can contain the values 'S' and 'C' or whatever. You do not need a composite primary key.
You can always concatenate these columns (PK and ContactType) on reports, e.g. C12345, S20000, (casting the integer to string) if you want to eliminate the column in order to save space (i.e. on the printed or displayed page), and everyone in your organization understands the convention that the first character of the entity id stands for the ContactType code.
This approach will leverage autoincrementing capabilities that are built into the database engine, simplify your PK and related code in the data layer, and make your program and database more robust.
First let us state that you can't do directly. If you try
create table dbo.t1 (
id varchar(10) identity,
);
the error message tells you which data types are supported directly.
Msg 2749, Level 16, State 2, Line 1
Die 'id'-Identitätsspalte muss vom
Datentyp 'int', 'bigint', 'smallint',
'tinyint' oder 'decimal' bzw.
'numeric' mit 0 Dezimalstellen sein
und darf keine NULL-Werte zulassen.
BTW: I tried to find this information in BOL or on MSDN and failed.
Now knowing that you can't do it the direct way, it is a good choice to follow #marc_s proposal using computed columns.
Instead of doing 'c0001, c0002' for customers and 's0001, s0002' for suppliers in one table, proceed in the following way:
Create one Auto-Increment field "id" of Data Type "int (10) unsigned".
Create another field "type" of Data Type "enum ('c', 's')" (where c=Customer, s=Supplier).
As "#PerformanceDBA" pointed out, you can then make the Primary Key Index for two fields "id" & "type", so that your requirement gets fulfilled with the correct methodology.
INSERT INTO Yourtable (yourvarcharID)
values('yourvarcharPrefix'+(
SELECT CAST((SELECT CAST((
SELECT Substring((
SELECT MAX(yourvarcharID) FROM [Yourtable ]),3,6)) AS int)+1)
AS VARCHAR(20))))
Here varchar column is prefixed with 'RX' then followed by 001, So I selected substring after that prefix of it and incremented the that number alone.
We can add Default Constraint Function with table definition to achieve this.
First create table -
create table temp_so (prikey varchar(100) primary key, name varchar(100))
go
Second create new User Defined Function -
create function dbo.fn_AutoIncrementPriKey_so ()
returns varchar(100)
as
begin
declare #prikey varchar(100)
set #prikey = (select top (1) left(prikey,2) + cast(cast(stuff(prikey,1,2,'') as int)+1 as varchar(100)) from temp_so order by prikey desc)
return isnull(#prikey, 'SB3000')
end
go
Third alter table definition to add default constraint -
alter table temp_so
add constraint df_temp_prikey
default dbo.[fn_AutoIncrementPriKey_so]() for prikey
go
Fourth insert new row into table without specifying value for primary column-
insert into temp_so (name) values ('Rohit')
go 4
Check out data in table now -
select * from temp_so
OUTPUT -
prikey name
SB3000 Rohit
SB3001 Rohit
SB3002 Rohit
SB3003 Rohit
you may try below code:
SET #variable1 = SUBSTR((SELECT id FROM user WHERE id = (SELECT MAX(id) FROM user)), 5, 7)+1;
SET #variable2 = CONCAT("LHPL", #variable1);
INSERT INTO `user`(`id`, `name`) VALUES (#variable2,"Jeet");
1st line to get last inserted Id by removing four character than increase one value and set to a variable1
2nd line to make complete id with four character prefix and assign to variable2
insert new value with generated new primary key = variable2
you should have minimum one data in this table to work above SQL
No. If you really need this, you will have to generate ID manually.
I am trying to create a Photo Album/Collection for each student in my ASP.NET website, using SQL Server tables.
Here are my table structure:
I want to store the image name within the Student_Images folder. Then each image will be linked to a Photo Collection. Each Photo Collection is linked to a group.
Then I want to display each group, any photo collections which are associated with that group, and then any images associated with that collection.
I have been able to display images related to each student, but I don't know how to add the 'Photo Collection' table between the Students and Images tables.
Here is how I would like it to display:
Here is how the tables would look populated:
Can anyone advise me as to how to go about making this connection?
There's an idea of a foreign key - where a row in a table refers to a row in another table. You definitely want to do this for a number of reasons, to ensure data integrity and to document your relationships for whoever works on the site.
You don't quite have enough information to get where you want to get. You don't have any linkage between photo albums and the images they contain. You'll need another table for that.
I kinda recommend that you use column names that don't repeat the table name. You'll find you can then see patterns more clearly in your schema. For example Photo_Collection.Photo_Collection_Id should probably be just Photo_Collection.Id. It's more concise, and it's obvious which Id you're talking about because you always use the table name when referring to that column.
So, to get you nearer to where you need to be, I'd recommend something like this:
create table dbo.Students
(
ID
int not null identity( 1, 1 )
constraint [Students.ID.PrimaryKey]
primary key clustered,
Name
nvarchar( 50 ) --> not very generous :-)
)
go
create index [Students.Name.Index] on dbo.Students( Name ) --> maybe unique?
go
create table dbo.Student_Images
(
ID
int not null identity( 1, 1 )
constraint [Student_Images.ID.PrimaryKey]
primary key clustered,
Student_ID
int not null
constraint [Student_Images.to.Student]
foreign key references dbo.Students( ID )
Filename
nvarchar( 250 ) null, --> null? really? each image should have a unique file name, dont you think?
Description
nvarchar( 250 ) null
)
go
create index [Student_Images.to.Students.Index] on dbo.Student_Images( Student_ID )
go
create table dbo.Photo_Collection
(
ID
int not null identity( 1, 1 )
constraint [Photo_Collection.ID.PrimaryKey]
primary key clustered,
Name
nvarchar( 250 ) null --> null? hmmm...could be hard to use
)
go
create index [Photo_Collection.Name.Index] on dbo.Photo_Collection( Name ) --> consider unique??
go
create table dbo.Photo_Collection_Images
(
Photo_Collection_ID
int not null
constraint [Photo_Collection_Images.to.Photo_Collection]
foreign key references dbo.Photo_Collection( ID ),
Student_Image_ID
int not null
constraint [Photo_Collection_Images.to.Student_Images]
foreign key references dbo.Student_Images( ID )
)
You didn't really describe your groups...and there are lots of questions still unanswered. For example, are Photo_Collections something that a student makes? If so, there should probably be a Student_ID in Photo_Collection with a foreign key to Students.
The table I added called Photo_Collection_Images relates your photo collections to the images. You'd include this table in any query that needs to display all the images in a given photo collection. I think that's the main missing bit for you. You'd do something similar for groups.
Also - FYI, pasting images of your text is kinda aggravating. Consider just pasting the text in and indenting it 4 spaces to get it to format correctly.
Edit:
To select, for example, all the images in a photo collection by student, you could do:
select
pc.Name PhotoCollectionName,
si.FileName FileName,
si.Description FileDescription,
s.Name StudentName
from
dbo.Photo_Collection pc
inner join
dbo.Photo_Collection_Images pci
on
pc.Id = pci.Photo_Collection_ID
inner join
dbo.Student_Image si
on
pci.Student_Image_ID = si.ID
inner join
dbo.Students s
on
si.Student_Id = s.ID
This is pretty much the base query for everything.
Make some changes in your Photo_Collection table, create an FK within your Photo_Collection of Student_ID, then you'll be able to show the photo collection of all the students because through Student_ID, you can have access to student table as well as Student_Images.
Hope it helps.
I am not sure if this is a duplicate or not as there have been several how to's for removing unique constraint. I feel like my question is just different enough to warrant a new question. I have C# code which builds up mysql queries. They end out looking like this:
CREATE table_B like table_A;
I then alter each of the newly created tables to add history details similar to this:
ALTER TABLE table_B
MODIFY COLUMN primary_column int(11) NOT NULL,
DROP KEY `PRIMARY`,
ENGINE = MyISAM,
ADD db_action_type VARCHAR(8) DEFAULT 'insert' FIRST,
ADD revision INT(6) NOT NULL AUTO_INCREMENT AFTER db_action_type,
ADD dt_datetime DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER revision,
ADD PRIMARY KEY(revision);
Essentially, I am creating history tables. The trouble I am having is that I cannot add additional rows if the table has a unique constraint. I could do some queries to get a list of unique constraint columns for each table and alter each column individually or as part of this alter table. I am just wondering if there is an easy way which does not require knowing any of the column names. Is there any sort of blanket effect drop unique attribute without caring about specifics?
I've taken over an ASP.NET application that needs to be re-written. The core functionality of this application that I need to replicate modifies a SQL Server database that is accessed via ODBC from third party software.
The third-party application creates files that represent printer labels, generated by a user. These label files directly reference an ODBC source's fields. Each row of the table represents a product that populates the label's fields. (So, within these files are direct references to the column names of the table.)
The ASP.NET application allows the user to create/update the data for these fields that are referenced by the labels, by adding or editing a particular row representing a product.
It also allows the occasional addition of new fields... where it actually creates a new column in the core table that is referenced by the labels.
My concern: I've never programmatically altered an existing table's columns before. The existing application seems to handle this functionality fine, but before I blindly do the same thing in my new application, I'd like to know what sort of pitfalls exist in doing this, if any... and if there are any obvious alternatives.
It can become problem when too many columns are added to tables, and you have to be careful if performance is a consideration (covering indexes are not applicable, so expensive bookmark lookups might be performed).
The other alternative is a Key-Value Pair structure: Key Value Pairs in Database design, but that too has it's pitfalls and you are better off creating new columns, as you are suggesting. (KVPs are good for settings)
One option I think is to use a KVP table for storing dynamic "columns" (as first mentioned by Mitch), join the products table with the KVP table based on the product id then pivot the results in order to have all the dynamic columns in the resultset.
EDIT: something along these lines:
Prepare:
create table Product(ProductID nvarchar(50))
insert Product values('Product1')
insert Product values('Product2')
insert Product values('Product3')
create table ProductKVP(ProductID nvarchar(50), [Key] nvarchar(50), [Value] nvarchar(255))
insert ProductKVP values('Product1', 'Key2', 'Value12')
insert ProductKVP values('Product2', 'Key1', 'Value21')
insert ProductKVP values('Product2', 'Key2', 'Value22')
insert ProductKVP values('Product2', 'Key3', 'Value23')
insert ProductKVP values('Product3', 'Key4', 'Value34')
Retrieve:
declare #forClause nvarchar(max),
#sql nvarchar(max)
select #forClause = isnull(#forClause + ',', '') + '[' + [Key] + ']' from (
select distinct [Key] from ProductKVP /* WHERE CLAUSE */
) t
set #forClause = 'for [Key] in (' + #forClause + ')'
set #sql = '
select * from (
select
ProductID, [Key], [Value]
from (
select k.* from
Product p
inner join ProductKVP k on (p.ProductID = k.ProductID)
/* WHERE CLAUSE */
) sq
) t pivot (
max([Value])' +
#forClause + '
) pvt'
exec(#sql)
Results:
ProductID Key1 Key2 Key3 Key4
----------- --------- --------- --------- -------
Product1 NULL Value12 NULL NULL
Product2 Value21 Value22 Value23 NULL
Product3 NULL NULL NULL Value34
It very much depends on the queries you want to run against those tables. The main disadvantage of KVP is that more complex queries can become very inefficient.
A "hybrid" approach of both might be interesting.
Store the values you want to query in dedicated columns and leave the rest in an XML blob (MS SQL has nice features to even query inside the XML) or alternatively in a KVP bag. Personally I really don't like KVPs in DBs because you cannot build application logic specific indixes anymore.
Just another approach would be not to model the specific columns at all. You create generic "custom attribute" tables like: Attribute1, Attribute2, Attribute3, Attribute4 (for the required data type etc...) You then add meta data to your database that describes what AttrX means for a specific type of printer label.
Again, it really depends on how you want to use that data in the end.
One risk is the table getting too wide. I used to maintain a horrible app that added 3 columns "automagically" when new values were added to some XML (for some reason it thought everything would be a string a date or a number- hence the creation of 3 columns).
There are other techniques like serializing a BLOB or designing the tables differently that may help.