How can I get the primary key value and put it in another column when I insert the data?
Here is my table schema:
CREATE TABLE IF NOT EXISTS [MyTable] (
[ID] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
[custom_ID] INTEGER NULL,
[Name] VARCHAR (200) NULL)
The query I have so far is:
INSERT INTO MyTable (custom_ID, Name)
values (
' {Here I need to get the primary key value, and then put it in custom_ID} ',
'someName')
Thanks!
Don't do that. You are violating all sorts of good database design principles by going this route. The primary key is supposed to represent the data that unique identifies a tuple (row). When you start having multiple copies of your primary key, you in turn defeat the entire purpose of the key.
As suggested by Chris:
CREATE TRIGGER MyTable_CustomID AFTER INSERT ON MyTable WHEN NEW.Custom_ID=NULL BEGIN
UPDATE MyTable SET Custom_ID=NEW.Id WHERE ROWID=NEW.ROWID;
END;
As far I can say you are trying to generate some custom Id based upon the PK. Like if my PK = 1 then you need a custom Id as ABCDEF/000/1 . So in that case you have to pick the immediate Id generate by the insert query and then run a update statement either in a trigger or just after the insert statement. Since its sql lite so you need to research a little bit to get identity similar to Scope_Identity() or ##Identity in Sql Server.
Try something like this:
insert [Order] (col1, col2, ...) values ('val1', 'val2', ...) -- Note: no ID is specified
declare #id int = scope_identity()
insert OrderDetail (order_id, col1, ...) values (#id, 'val1', ...)
Related
I want to add a script post-deployment to insert data. But when I execute this script below I got this message error:
Must declare the table variable "#TempTable".
The purpose of this script it's to force adding Id which is a primary key, I know that we can use:
SET IDENTITY_INSERT [dbo].[TableName] ON;
GO
and after the Merge
SET IDENTITY_INSERT [dbo].[TableName] OFF;
GO
But it's not working for me I can't deploy my DacPac
DECLARE #vehicleType TABLE(
[VehicleTypeId] BIGINT,
[Name] NVARCHAR(200) NOT NULL
);
INSERT INTO #VehicleType ([VehicleTypeId], [Name])
VALUES(1,'Automobile'),(2,'HeavyVehicle'),(3,'Motorcycle')
SET IDENTITY_INSERT [dbo].[VehicleType] ON;
GO
MERGE INTO [dbo].[VehicleType]
USING #VehicleType as vhlt
ON ([dbo].[VehicleType].[VehicleTypeId] = vhtl.[VehicleTypeId] and [dbo].[VehicleType].[Name] = vhlt.[Name])
WHEN NOT MATCHED THEN
INSERT VALUES ([VehicleTypeId], [Name]);
SET IDENTITY_INSERT [dbo].[VehicleType] OFF;
GO
The first thing that I need to point out is that a lookup table such as this should not have an auto-number (IDENTITY) column. Auto-number values are synthetic and should only be used when you don't care what the value is, just that it's unique. That works well when users can add values to the lookup set.
For non-user-serviceable lookup tables, like your VehicleType, you do care what the VehicleTypeId values are so auto-number works against you. If you can remove the IDENTITY property from the column definition, you absolutely should. If you can't, you're stuck using IDENTITY_INSERT. Just remember not to use IDENTITY in future lookup table designs.
There's a way to do this without the table variable: use a CTE with the MERGE statement.
The CTE will contain all the values you want using unioned static SELECT statements (using UNION ALL skips duplicate checks since you know there won't be any duplicates).
The MERGE statement uses the CTE as its source table.
The ON clause in the MERGE statement should only compare the primary key. If a value in a non-key column was changed, it would cause a new row to be inserted; the new row would violate the primary key. Instead, update the Name column to the value in the CTE. To do that, I've added a WHEN MATCHED clause below.
I suggest always using dst and src as aliases in a MERGE statement. The MERGE statement can involve a lot of different parts. If you use the same aliases for the source and target/destination tables, that's one less bit of complexity you have to worry about. I've seen this actually help developers learn the syntax and this technique.
I like to name the CTE src so I don't actually have to alias it.
-- Only IF you cannot remove the IDENTITY property from VehicleTypeId:
SET IDENTITY_INSERT [dbo].[VehicleType] ON;
GO
-- Stage the data and merge it into the table in one shot:
WITH [src] ([VehicleTypeId], [Name])
AS
(
SELECT 1, 'Automobile'
UNION ALL SELECT 2, 'HeavyVehicle'
UNION ALL SELECT 3, 'Motorcycle'
)
MERGE INTO [dbo].[VehicleType] AS [dst]
USING [src]
ON [dst].[VehicleTypeId] = src.[VehicleTypeId]
WHEN NOT MATCHED BY TARGET THEN
INSERT ([VehicleTypeId], [Name])
VALUES ([src].[VehicleTypeId], [src].[Name])
WHEN MATCHED AND ([dst].[Name] <> [src].[Name]) THEN
UPDATE
SET [Name] = [src].[Name]
;
GO
-- Again, only IF you cannot remove the IDENTITY property from VehicleTypeId:
SET IDENTITY_INSERT [dbo].[VehicleType] OFF;
GO
When I write a script like this that I intend to run from SSMS, I include an OUTPUT clause so I can see what happened and verify that it was right.
...
OUTPUT $action AS [*Action],
COALESCE([inserted].[VehicleTypeId], [deleted].[VehicleTypeId]) AS [=VehicleTypeId],
[deleted].[Name] AS [-Name],
[inserted].[Name] AS [+Name]
-- Repeat deleted and inserted AS -/+ for remaining non-key columns.
;
A note about the prefixes I've used in the aliases:
* for metadata columns (really, it's only ever *Action).
= for the primary key columns. They won't change, so only one per column in the key.
- for the old values (e.g. -Name).
+ for the new values (e.g. +Name).
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 have two tables , one to one relationship i makes DetailsID of projectDetails FK in ID of projects table:
projects:
ID, //has FK With DetailsID in Details table & auto identity (1,1)
ProjectName,
Areas,
PaymentSystem,
ReceivedDate,
PropertyClassification,
ProjectImage
ProjectDetails:
DetailsID ,auto identity ( 1,1)
ProjectDetailName,
ProjectDetailImage
I am trying to insert new record in projects table , gives me this error at this line of code :
con.Open();
comm.ExecuteNonQuery(); // when execute
System.Data.SqlClient.SqlException: 'The INSERT statement conflicted with the FOREIGN KEY constraint "FK_Projects_ProjectDetails". The conflict occurred in database "AlamaarRealEstate", table "dbo.ProjectDetails", column 'DetailsID'.
and this is my stored to insert :
ALTER proc [Insert_Project]
#Projectname NVARCHAR(MAX) ,
#areas NVARCHAR(MAX) ,
#Paymentsystem NVARCHAR(MAX) ,
#Receiveddate date ,
#Classification NVARCHAR(MAX) ,
#Projectimage Nvarchar(MAX)
as
INSERT INTO dbo.Projects
(
ProjectName,
Areas,
PaymentSystem,
ReceivedDate,
PropertyClassification,
ProjectImage
)
VALUES
(
#Projectname ,
#areas,
#Paymentsystem ,
#Receiveddate ,
#Classification,
#Projectimage
)
The question explains the answer. Referential Integrity is not maintained properly, you are trying to insert into a child table for which the master value does not exist. Please insert values to Project_details first. This will resolve your issue. If you did not what this to throw an error, just check the existence of the DetailID in Projects table before inserting.
Without more detail your question is hard to answer. For instance in your procedure you do explicit column naming for inserts and LEAVE OUT the column you are having a problem with. So if the proc was wrong it would be bombing with your example foreign key constraint error. But you are not even listing the 'ID' field to insert into with the procedure. So this is common if you are using an 'Identity' field to self seed, but you are claiming it is a foreign key. So like others have commented, without more code to show the exact way your tables are made it's hard to guess. Here is a self extracting example you could run that shows if a column is nullable and I had a key constraint it would work. Without the exact code of the tables as well as the proc it is hard to tell. What you gave is pseudo code.
USE Tester --just a test database I have, you can use whatever database you want
GO
IF OBJECT_ID('Projects') IS NOT NULL
DROP TABLE Projects
IF OBJECT_ID('ProjectDetails') IS NOT NULL
DROP TABLE ProjectDetails
create TABLE ProjectDetails
(
DetailsID INT CONSTRAINT PK_DetailsId PRIMARY KEY,
ProjectDetailName VARCHAR(32)
)
CREATE TABLE Projects
(
Id INT CONSTRAINT FK_Projects_ProjectDetails FOREIGN KEY (Id) REFERENCES ProjectDetails(DetailsId),
ProjectName varchar(32)
)
GO
IF OBJECT_ID('Insert_Project') IS NOT NULL
DROP PROC Insert_Project
GO
Create proc Insert_Project
#Projectname NVARCHAR(MAX)
as
INSERT INTO dbo.Projects ( ProjectName )
VALUES ( #Projectname )
GO
Select *
From dbo.Projects
EXEC dbo.Insert_Project #Projectname = N'Test' -- nvarchar(max)
Select *
From dbo.Projects
I'm doing an insert into a SQL Server CE table where the primary key is auto-generated, how would I go about getting the primary key after the insertion completes?
Assuming your primary key is an identity field, you can use the query SELECT ##IDENTITY after you insert your new row.
You have to be careful doing this. What you need to do is in a transaction set your select to:
IDbCommand.CommandText += "; select scope_identity()";
object rtn = cmd.ExecuteScalar();
return (long)(decimal)rtn;
A single select inside a transaction is key because calling select scope_identity() is not the primary key of your insert, it is the most recent insert. So you can otherwise end up with the primary key of another insert that occurred after your insert, but before your request for the most recent PK insert.
Note: The scope_identity() may (repeat may) now be tied to the IDbCommand, but in the past it was not and following the above is safe.
you can find your primary key is an identity field from table
SELECT IDENT_CURRENT(‘tablename’)
http://blog.sqlauthority.com/2007/03/25/sql-server-identity-vs-scope_identity-vs-ident_current-retrieve-last-inserted-identity-of-record/
Is is possible to get new values for Id (IDENTITY) before inserting data in a table ?
Is is possible to write something like that :
INSERT INTO Table1
SELECT *GET_NEW_IDENTITY*, Field1, Field2 FROM Table2
I need the values of Id because I want to insert data in Table1 and, just after, insert data in another table which has a foreign key linked to Table1 (with Id)
IDENT_CURRENT. Returns the last identity value generated for a specified table or view. The last identity value generated can be for any session and any scope.
SCOPE_IDENTITY. Returns the last identity value inserted into an identity column in the same scope. A scope is a module: a stored procedure, trigger, function, or batch.
OUTPUT. Returns information from, or expressions based on, each row affected by an INSERT, UPDATE, DELETE, or MERGE statement. [...] The OUTPUT clause may be useful to retrieve the value of identity or computed columns after an INSERT or UPDATE operation.
you can also have the insert statement return the newly inserted value for later use. for example
create table demo( Id int identity primary key, data varchar(10))
go
insert into demo(data) output inserted.Id values('something')
No, because it is the act of adding a row which creates the new identity value.
To do what you want,
SELECT newid = ##identity FROM table
just after the INSERT
Why would you need to get the identity value before doing the insert? Just do the insert to Table2 returning SCOPE_IDENTITY() and then use the resulting Id value for your insert to Table1.
This is just fast demo. You can use new ID for insert for update, insert into another table, query, etc. in another way. Hoping I did not insert errors into script during formatting, editing post
-- run [1] before this script once to have environment
--create temporary table once if not dropped after
-- really only ID field is needed, the others are for illustration
create table #temp_id (Id int, d1 int, d2 int)
select * from Table2;-- this is read-only, filled once here source
select * from Table1;--interesting for following runs
insert into Table1
OUTPUT INSERTED.id
-- really only ID is needed, the rest is for illustration
, inserted.d1, inserted.d2 INTO #temp_id
select field1, field2, null-- null to be merged later
-- or inserted/updated into another table
from Table2;
select * from Table1;
select * from #temp_id;
MERGE Table1 AS TARGET
USING #temp_id AS SOURCE
ON (TARGET.id = SOURCE.id)
WHEN MATCHED
--AND OR are redundant if Table1.ID is PK
THEN
UPDATE SET TARGET.IDnew = SOURCE.id;
select * from Table1;
--drop table #temp_id
--drop table table1
--drop table table2
[1]
Reproducing the tables from question and filling with data
create table Table1( Id int identity primary key, d1 int, d2 int, IDnew int)
create table Table2( field1 int, field2 int)
insert into table2 values(111,222)
insert into table2 values(333,444)
IDENT_CURRENT('tableName') returns the current value of the identity for the given table. The identity value that will be assigned on Insert will be IDENT_CURRENT('tableName') + IDENT_INCR('tableName').
SELECT IDENT_CURRENT('tableName') + IDENT_INCR('tableName')