Vertica NEXTVAL() function - how to invoke it on a record level by condition? - sequence

I'm using Vertica Analytic Database v9.2.1-20,
and trying to get next sequence value by condition on a record level, with NEXTVAL() function.
here is the example
CREATE SEQUENCE v_seq START 1;
CREATE TABLE test_seq (id INT, flag int);
INSERT INTO test_seq VALUES (1, 0);
INSERT INTO test_seq VALUES (2, 1);
INSERT INTO test_seq VALUES (3, 1);
INSERT INTO test_seq VALUES (4, 0);
INSERT INTO test_seq VALUES (5, 0);
INSERT INTO test_seq VALUES (6, 1);
INSERT INTO test_seq VALUES (7, 1);
SELECT id,
flag,
CASE
WHEN flag = 0 THEN NEXTVAL('v_seq')
ELSE CURRVAL('v_seq')
END as group_id
FROM test_seq
ORDER BY ID;
The expected results :
id flag group_id
1 0 1
2 1 1
3 1 1
4 0 2
5 0 3
6 1 3
7 1 3
The actual results :
id flag group_id
1 0 1
2 1 2
3 1 3
4 0 4
5 0 5
6 1 6
7 1 7
It looks like the NEXTVAL() function is being invoked regardless WHEN condition.
Is there any way to invoke it by condition ?
Thanks!

Several tests of mine - in Vertica 10 that is - caused the same behaviour you report above.
There is no way to get it to do what you want, as it seems.
As said elsewhere, you could use CONDITIONAL_TRUE_EVENT(flag=0) OVER(ORDER BY id) to obtain your desired result.
There is the risk of being slow, as you have no PARTITION BY in your OLAP window specification, therefore running single-threaded, in the case of a big table.
But a big table will be segmented.
In a segmented table, with seq.nextval, each portion/segment of the table will start the sequence you use at a different multiple of, by default, 250,000. In node 1, the sequence starts at 1, in node 2, the sequence starts at 250,000, in node 3 at 500,000, etc. If the table is not segmented, the sequence generator will cause a single-threaded process, too, thus penalising your performance. So you are somehow between a rock and a hard place....
If the table is segmented, you could parallelise the CONDITIONAL_TRUE_EVENT() call, by using the base table's segmentation criterion as the PARTITION BY criterion in the OLAP function call.

I ran into a similar problem. The way vertica handles sequences for multiple nodes is what causes the core problem (even with just one node). Since the system has to perform a catalog lock (across all nodes in the cluster), sequences are allocated in large blocks. You can change the block size but that's not particularly helpful.
To solve the problem, mainly getting a controlled quantity of sequenced values we cached them and allocated them from Java memory. Once the database allocates them, they will not be re-allocated. This will get you closer to what you are trying to achieve but takes more work.

Related

Missing expression Error with Insert Select query in Oracle SQL

i got this error with Oracle SQL with Insert Select query and don't where the error comes from
the SQL Query is:
insert into GroupScenarioAction (ID, ID_UUID, GPSCENARIO_UUID, ACTION, VERSION)
(select DEFAULT , '0', ACTION.ID_UUID, '5310AFAA......', '1', ACTION_ID, '0'
from ACTION where ACTION.id not in (select ACTION FROM GroupScenarioAction where
GPSCENARIO = '1'));
the error is ORA-00936: missing expression Position 129
It is difficult to assist because
you posted relevant data as images (why do you expect us to type all of that so that we could try it?) instead of code (which can easily be copy/pasted and used afterwards)
code you posted (the insert statement itself) uses columns that don't exist in any tables whose description you posted
for example, insert inserts into GroupScenarioAction, but there's no such table there; maybe it is goroohscenarioaction? Or, there's no action_id column in the action table
you're inserting values into 5 columns, but select statement contains 7 columns; that raises ORA-00913: too many values error, you don't even come to the missing expression error
Shortly, as if you tried to do everyhing you could to prevent us from helping you.
One of comments you posted says
It's the primary key so where are those values supposed to come from?
That's the default keyword in
insert into GroupScenarioAction (ID, ...)
(select DEFAULT, ...
-------
this
Looks like the ID column is created as an identity column whose value is autogenerated (i.e. Oracle takes care about it), which also means that you're on Oracle 12c or above (there was no such an option in lower versions). On the other hand create table goroohscenarioaction statement doesn't suggest anything like that.
Anyway: if you do it right, it works. I created two sample tables with a minimum column set, just to make insert work. Also, as I'm on 11gXE (which doesn't support identity columns, I'm inserting a sequence value which is, basically, what identity column uses in the background anyway):
SQL> create table groupscenarioaction
2 (id number,
3 id_uuid raw(255),
4 gpscenario_uuid raw(255),
5 action number,
6 version number
7 );
Table created.
SQL> create table action
2 (id_uuid raw(255),
3 id number
4 );
Table created.
SQL> create sequence seq;
Sequence created.
Insert you posted; I commented out columns that either don't exist or are superfluous. It works; though, didn't insert anything as my table(s) are empty, but it doesn't raise any error:
SQL> insert into GroupScenarioAction
2 (ID, ID_UUID, GPSCENARIO_UUID, ACTION, VERSION)
3 (select 1 /*DEFAULT*/ , '0', ACTION.ID_UUID, '5310AFAA......', '1' --, id /*ACTION_ID*/, '0'
4 from ACTION
5 where ACTION.id not in (select ACTION FROM GroupScenarioAction
6 where gpscenario_uuid/*GPSCENARIO*/ = '1'));
0 rows created.
Beautified:
SQL> insert into groupscenarioaction
2 (id, id_uuid, gpscenario_uuid, action, version)
3 (select seq.nextval, '0', a.id_uuid, '5310AFAA......', '1'
4 from action a
5 where a.id not in (select g.action
6 from groupscenarioaction g
7 where g.gpscenario_uuid = '1'));
0 rows created.
SQL>
Now that you know a little bit more about what's bothering use to help you, and if what I wrote isn't enough, consider editing the original question you posted (simply remove everything that's wrong and write something that is true and we can use).

Prevent consecutive duplicate values without a trigger

Within a group, I'd like to prevent INSERTs of consecutive duplicate values, where "consecutive" is defined by a simple ORDER BY clause.
Imagine a set of experiments which is regularly sampling values from a sensor. We only want to insert a value if it is new for that experiment.
Note that older values are allowed to be duplicates. So this is allowed:
id experiment value
1 A 10
2 A 20
3 A 10
but this is not:
id experiment value
1 A 10
2 A 10
I know how to find the previous value per experiment:
SELECT
*,
lag(sample_value) OVER experiment_and_id
FROM new_samples
WINDOW experiment_and_id AS (
PARTITION BY experiment
ORDER BY id
);
From the docs I know that CHECK constraints are not allowed to use other rows in their checking:
PostgreSQL does not support CHECK constraints that reference table data other than the new or updated row being checked. While a CHECK constraint that violates this rule may appear to work in simple tests, it cannot guarantee that the database will not reach a state in which the constraint condition is false (due to subsequent changes of the other row(s) involved). This would cause a database dump and reload to fail. The reload could fail even when the complete database state is consistent with the constraint, due to rows not being loaded in an order that will satisfy the constraint. If possible, use UNIQUE, EXCLUDE, or FOREIGN KEY constraints to express cross-row and cross-table restrictions.
If what you desire is a one-time check against other rows at row insertion, rather than a continuously-maintained consistency guarantee, a custom trigger can be used to implement that. (This approach avoids the dump/reload problem because pg_dump does not reinstall triggers until after reloading data, so that the check will not be enforced during a dump/reload.)
The EXCLUDE constraint looks promising, but is primarily for cases where the test is not equality. And I'm not sure if I can include window functions in there.
So I'm left with a custom trigger but this seems like a bit of a hack for what seems like a fairly common use case.
Can anyone improve on using a trigger?
Ideally, I'd like to be able to just say:
INSERT ....
ON CONFLICT DO NOTHING
and have Postgres deal with the rest!
Minimum working example
BEGIN;
CREATE TABLE new_samples (
id INT GENERATED ALWAYS AS IDENTITY,
experiment VARCHAR,
sample_value INT
);
INSERT INTO new_samples(experiment, sample_value)
VALUES
('A', 1),
-- This is fine because they are for different groups
('B', 1),
-- This is fine because the value has changed
('A', 2),
-- This is fine because it's different to the previous value in
-- experiment A.
('A', 1),
-- Two is not allowed here because it's the same as the value
-- before it, within this experiment.
('A', 1);
SELECT
*,
lag(sample_value) OVER experiment_and_id
FROM new_samples
WINDOW experiment_and_id AS (
PARTITION BY experiment
ORDER BY id
);
ROLLBACK;
If the samples will not change, then the restriction cited in the docs will not be relevant to your use case.
You can create a function to accomplish this:
create or replace function check_new_sample(_experiment text, _sample_value int)
returns boolean as
$$
select _sample_value != first_value(sample_value)
over (partition by experiment
order by id desc)
from new_samples
where experiment = _experiment;
$$ language sql;
alter table new_samples add constraint new_samples_ck_repeat
check (check_new_sample(experiment, sample_value));
Example inserts:
insert into new_samples (experiment, sample_value) values ('A', 1);
INSERT 0 1
insert into new_samples (experiment, sample_value) values ('B', 1);
INSERT 0 1
insert into new_samples (experiment, sample_value) values ('A', 2);
INSERT 0 1
insert into new_samples (experiment, sample_value) values ('A', 1);
INSERT 0 1
insert into new_samples (experiment, sample_value) values ('A', 1);
ERROR: new row for relation "new_samples" violates check constraint "new_samples_ck_repeat"
DETAIL: Failing row contains (5, A, 1).

Auto increment even and odd for two databases for synchronization without affecting auto increment property

Need quick help. I am having my database with bigint auto increment property. I have database on two location's which need to synchronized. As bigint is not good choice for synchronization because of possibility primary key replica on different sites. I can not move on with GUID because for that i need to change my code as well as database which is not possible for me.
Right now i have two location only for database, so i think if can make my primary key auto increment to be always even at one location and to be odd at other location. It can solve my issue in quick manner.
How can i do it using computed column specification or by any other way. For synchronization i am using Microsoft sycn framework.
If i use identity(1,2) A server or identity(2,2) B server after synchronization it disturbs next increment value. For example if at A server max id 3 and at B server current id is 4. After sync max id on A server will be now 4. I want new id on A server should be 5 only but in reality it inserts 6. How can i solve this issue
Here is a very simple solution, but it will work only for two servers. It can't be easily extended for more servers.
The good thing about it is that it doesn't use CHECKIDENT to reseed the tables and you don't need to worry about simultaneously running transactions to get the accurate MAX ID to feed into CHECKIDENT.
Also, MSDN warns that identity property on a column does not guarantee the following:
Consecutive values after server restart or other failures – SQL Server
might cache identity values for performance reasons and some of the
assigned values can be lost during a database failure or server
restart. This can result in gaps in the identity value upon insert. If
gaps are not acceptable then the application should use its own
mechanism to generate key values. Using a sequence generator with the
NOCACHE option can limit the gaps to transactions that are never
committed.
If you choose a solution that is based on reseeding identity using CHECKIDENT you'd better double check that it works correctly in such cases.
Also, to run CHECKIDENT you may need specific permissions:
Caller must own the table, or be a member of the sysadmin fixed server
role, the db_owner fixed database role, or the db_ddladmin fixed
database role.
Solution
My main idea is that on the first server you use IDENTITY(1,1) and on the second server you use IDENTITY(-1,-1). Rather than trying to make IDs odd and even they will be positive and negative.
Here is a script that proves that it works as intended without any extra work.
-- Sample data
CREATE TABLE #T1 (ID bigint IDENTITY(1,1), V1 int);
CREATE TABLE #T2 (ID bigint IDENTITY(-1,-1), V2 int);
INSERT INTO #T1 VALUES (11);
INSERT INTO #T1 VALUES (12);
INSERT INTO #T1 VALUES (13);
INSERT INTO #T1 VALUES (14);
INSERT INTO #T2 VALUES (21);
INSERT INTO #T2 VALUES (22);
INSERT INTO #T2 VALUES (23);
SELECT * FROM #T1;
SELECT * FROM #T2;
We start with this sample data in our tables:
#T1
ID V1
1 11
2 12
3 13
4 14
#T2
ID V2
-1 21
-2 22
-3 23
Perform the sync
-- Insert into T1 new values from T2
SET IDENTITY_INSERT #T1 ON;
MERGE INTO #T1 AS Dst
USING
(
SELECT ID, V2
FROM #T2
) AS Src
ON Dst.ID = Src.ID
WHEN NOT MATCHED BY TARGET
THEN INSERT (ID, V1)
VALUES (Src.ID, Src.V2);
SET IDENTITY_INSERT #T1 OFF;
-- Insert into T2 new values from T1
SET IDENTITY_INSERT #T2 ON;
MERGE INTO #T2 AS Dst
USING
(
SELECT ID, V1
FROM #T1
) AS Src
ON Dst.ID = Src.ID
WHEN NOT MATCHED BY TARGET
THEN INSERT (ID, V2)
VALUES (Src.ID, Src.V1);
SET IDENTITY_INSERT #T2 OFF;
SELECT * FROM #T1;
SELECT * FROM #T2;
Result of the sync - two identical tables
#T1
ID V1
1 11
2 12
3 13
4 14
-1 21
-2 22
-3 23
#T2
ID V2
-1 21
-2 22
-3 23
1 11
2 12
3 13
4 14
Insert more data to check how identity works after the sync
-- Insert more data into T1 and T2
INSERT INTO #T1 VALUES (15);
INSERT INTO #T1 VALUES (16);
INSERT INTO #T2 VALUES (24);
INSERT INTO #T2 VALUES (25);
INSERT INTO #T2 VALUES (26);
SELECT * FROM #T1;
SELECT * FROM #T2;
-- Clean up
DROP TABLE #T1;
DROP TABLE #T2;
Generated identities after the sync
#T1
ID V1
1 11
2 12
3 13
4 14
-1 21
-2 22
-3 23
5 15
6 16
#T2
ID V2
-1 21
-2 22
-3 23
1 11
2 12
3 13
4 14
-4 24
-5 25
-6 26
You can see that new identities in T1 continue to be positive and new identities in T2 continue to be negative.
Big Edit: (Much better) Option 1:
(Additional note: #VladimirBaranov mentioned this in the comments, and I missed it, but here's a fleshing out of how to use a SEQUENCE in this situation anyway)
My original idea is further down this answer, and would still be potentially viable, but I think this newer option will fit the bill exactly for however many servers you need. It was bothering me that I knew there was a right way to do this in TSQL, and I could not remember what it was. My brain finally dredged it up today: SEQUENCE. SQL Server 2012 and 2014 allow you to define a sequence to generate a series of numbers for use in your tables:
CREATE SEQUENCE oddNums
START WITH 1
INCREMENT BY 2;
GO
CREATE SEQUENCE evenNums
START WITH 0
INCREMENT BY 2;
GO
Then instead of AUTO INCREMENTing your PK's, give them a DEFAULT value from the SEQUENCE (these are the tables from the fiddle linked below):
CREATE TABLE oddMirror (
[id] int PRIMARY KEY DEFAULT NEXT VALUE FOR oddNums,
[data] varchar(7)
);
CREATE TABLE evenMirror (
[id] int PRIMARY KEY DEFAULT NEXT VALUE FOR evenNums,
[data] varchar(7)
);
These sequences are totally unaffected by merges, and will continue to generate odd or even numbers forever no matter what the latest PK in the table is.
Here is a SQLFiddle of this in action.
Note that you can't define the column as IDENTITY if you do this (because of the DEFAULT clause), so you will have to be careful about inserting into your id column, but otherwise this should be about as straightforward as it gets.
This could be done with as many servers as you want, just adjust how much each SEQUENCE increments by and where it starts from, but you would have a hard (not impossible) time adding additional servers to the mix once your SEQUENCEs were defined.
Also, here is an MSDN blog that discusses alternative strategies for simulating a SEQUENCE on prior versions of SQL Server.
(Not as good) Option 2:
(Note: this is my original answer) I have been playing with this some this evening, and depending on how you have things set up, I think you could get away with reseeding the table on each server after the sync is done, based on the current highest id in the table. You would just have to do it slightly differently for each server to keep new ids odd on one and even on the other.
So you have:
CREATE TABLE oddMirror
(id INT NOT NULL IDENTITY(1,2),
data NVARCHAR(10))
GO
and
CREATE TABLE evenMirror
(id INT NOT NULL IDENTITY(2,2),
data NVARCHAR(10)
GO
After you sync the two tables, you don't know if the current identity seed is odd or even, so you need to reset it on each table to the correct "next" value for the server. So, on oddMirror:
DECLARE #maxId INT
DECLARE #newSeed INT
SET #maxId = (SELECT MAX(id) FROM oddMirror)
SET #newSeed = (SELECT CASE WHEN #maxId % 2 = 1 THEN #maxId ELSE #maxId -1 END)
DBCC CHECKIDENT('dbo.oddMirror', RESEED, #newSeed)
GO
And an almost identical process on evenMirror:
DECLARE #maxId INT
DECLARE #newSeed INT
SET #maxId = (SELECT MAX(id) FROM evenMirror)
SET #newSeed = (SELECT CASE WHEN #maxId % 2 = 0 THEN #maxId ELSE #maxId -1 END)
DBCC CHECKIDENT('dbo.evenMirror', RESEED, #newSeed)
GO
So basically, on oddMirror we are saying, "Get the current max id. if it's odd, don't change it, but if it's even, back it up by one."
Then do the same thing on 'evenMirror', except check whether the max id is even instead of odd.
So as an example, take this data:
oddMirror
1,"one"
3,"three"
5,"five"
and
evenMirror
2,"two"
4,"four"
6,"six"
8,"eight"
(notice that evenMirror has more rows)
After a sync, each table would look like this:
oddMirror
1,"one"
2,"two"
3,"three"
4,"four"
5,"five"
6,"six"
8,"eight"
--evenMirror looks the same as this now
Running things through the above queries:
MAX(id) on oddMirror is 8. 8 % 2 = 0, so set #newSeed = 8 - 1 = 7, meaning the next row in oddMirror will get id = 9.
MAX(id) on evenMirror is also 8, but the query is slightly different. 8 % x = 0 so set #newSeed = 8, meaning the next row in 'evenMirrorwill getid = 10`
id = 7 will be skipped in this scenario, but I'm guessing that's not a tremendous concern.
If you then queried:
INSERT INTO oddMirror (data) VALUE ("data")
GO
INSERT INTO evenMirror (data) VALUE ("otherData")
GO
Tables would look like this:
oddMirror
1,"one"
2,"two"
3,"three"
4,"four"
5,"five"
6,"six"
8,"eight"
9,"data"
and
evenMirror
1,"one"
2,"two"
3,"three"
4,"four"
5,"five"
6,"six"
8,"eight"
10,"otherData"
This could theoretically be expanded out to accommodate more number of servers by changing what modulo you take and adding additional WHENs to the CASE statement for each possibility, although that would certainly get cumbersome to maintain. But, we already knew that the right solution (GUIDs) is not available here, and if you're reading this far, the next best solution (SEQUENCEs) may not be available, so whatever we come up with is inevitably going to be cumbersome.
The biggest drawback to this approach is that the tables must be locked until the sync process is complete. If a write comes in before sync is complete and ids are reseeded, there will almost certainly be a collision. If the tables are not written to very often, or you are already locking them for syncing, or if you have a significant "dead" spot in your daily cycle (like 3-4 a.m. or something) where this could be done without serious disruption, it may not be that big of a deal, but only you will know how viable this is.
So, your setup may or may not make this possible, but I have been playing with this quite a bit tonight in my sandbox db, and it seems to work well to make sure that new id's are always odd in one db and always even in the other.
An option, have one server set with identity(1,1), and the other set with identity(-1,-1). Identities will not overlap, and copying data from one server to the other would not affect the "next" id or reseeding.
Doesn't work for more than two servers, of course.
You have two source table for inserting to one destination table :
So I suggest you to do these :
[pkId]: Have an identity field in destination table as PK.
[src]: Add an integer -or any other as you want- field and update it from 1st. source data by1and for the 2nd one by2`.
[Id]: You have also a field that comes from sources.
[nId]: Add a bigint field that is null.
Now use this query to have your enumerated Id in nId field:
Update <table>
Set nId = isnull((select count(ti.*) from <table> as ti where ti.pkId < <table>.pkId), 0) + 1
For running this query after any inserts you can use triggers.
I think with this solution you have all data you need.
Edit
some results:
pkId | src | Id | nId before query | nId after query
-----+-----+----+------------------+--------------------
1 | 1 | 1 | null | 1
2 | 2 | 1 | null | 2
3 | 1 | 2 | null | 3
5 | 2 | 2 | null | 4
6 | 2 | 3 | null | 5
8 | 1 | 3 | null | 6
I think that the even/odd approach is making this very hard. Also, as you add rows to each node, you will have page-split issues, especially if your PK is the clustered index.
Are these tables replicated using peer-to-peer replication or are they manually synchronized? The page split issues would come into play if there is replication involved.
Why not use ranges of numbers for each node? 1-X for node 1, and X+1-Y for Node#2? Estimate your row volume and set the ranges so large that overlap won't occur.
Example for BIGINT:
Node 1: 1-200000000000 (200 billion rows)
Node 2: 200000000001-600000000000 (400 billion rows)
Leaving 600000000001 and up for future use. Caveat, the Identity does not have a maximum value, you need to police that manually.
To set the identity value to the correct number use DBCC CHECKIDENT with the RESEED option. This would also work if you're married to the even/odd scenario.
This also have the advantage of not page splitting once per insert, especially if the activity per node is not evenly balanced.

SQL Server Identity column does not behave properly?

I created a table named tblEmployees with code -
Create table tblEmployees
(
EmployeeID int identity primary key,
Name nvarchar(30),
Salary float,
Gender tinyint
)
Then I inserted values-
insert into tblEmployees values ('Akmal', 5000, 0)
insert into tblEmployees values ('Shakira', 6000, 1)
insert into tblEmployees values ('Kiron', 7000, 2)
insert into tblEmployees values ('Jamil', 5500, 0)
insert into tblEmployees values ('Faul', 4800, 4)
But, when the values are shown -
EmployeeID Name Salary Gender
2 Akmal 5000 0
3 Shakira 6000 1
4 Kiron 7000 2
5 Jamil 5500 0
7 Faul 4800 4
My question is, why EmployeeID column started with 2? And where is 6? Shouldnt this be incremented automatically?
Do not rely on IDENTITY columns to produce a contiguous set of values with no gaps. Period. This is not guaranteed at all; several things can cause gaps such as rollbacks, deletes, reseeds, etc. I don't believe you reproduced this problem with that exact code above; there was probably other activity in between those INSERT statements.
For such surrogate and meaningless values you really shouldn't care if there are gaps or not. If you care about gaps, use a different technique (e.g. a serializable max()+1 solution) - just be aware that you trade gaps for scalability / concurrency concerns. The other answer (which you accepted, but I suspect will get deleted) said:
If you want to have an identity column with dependable and specified values then you need to set IDENTITY_INSERT to ON on that column, insert your values (with specific ID values) and then set IDENTITY_INSERT to OFF.
This only works if you already know the values you want to insert into that column. Which defeats the purpose of the IDENTITY property in the first place. If you don't already know what values to insert (e.g. what is the "next" ID), it means you need to SELECT MAX() from the table, and add 1 to it. Which means the whole thing needs to be serializable, otherwise someone else can read your same MAX() value and add the same +1 to it. So aside from making the IDENTITY property useless if you're always going to override the generated value anyway, it also kills scalability by effectively limiting concurrency to 1. I highly recommend you strongly weigh that approach before implementing it.
What I suggest you do instead, is use an IDENTITY column, and don't be hung up on gaps. They're going to happen, there's not much you can do about it, and it shouldn't really be a concern anyway. Who cares if there is no employee #6?
Your T-SQL script is incomplete because my IDENTITY values are generated starting with 1 (and ending with 5).
Note #0: I'm only try to describe some reasons for those missing IDENTITY values.
Note #1: Don't run this script on a production server.
Note #2: Using IDENTITY within a column definition means IDENTITY(1,1) <=> IDENTITY(seed value/initial value=1,increment value=1).
Note #3: You should avoid using DBCC CHECKIDENT if you aren't aware about the consequences of this command.
The first missing value (a possible explanation):
why EmployeeID column started with 2?
Run the following script:
IF OBJECT_ID(N'dbo.tblEmployees') IS NOT NULL
DROP TABLE dbo.tblEmployees;
GO
Create table tblEmployees
(
EmployeeID int identity primary key,
Name nvarchar(30),
Salary float,
Gender tinyint
)
GO
insert into tblEmployees values ('Akmal', 5000, 0)
insert into tblEmployees values ('Shakira', 6000, 1)
insert into tblEmployees values ('Kiron', 7000, 2)
insert into tblEmployees values ('Jamil', 5500, 0)
insert into tblEmployees values ('Faul', 4800, 4)
GO
SELECT SCOPE_IDENTITY() AS [Last IDENTITY #1];
/*
Last IDENTITY #1
----------------
5
*/
GO
At this moment the last IDENTITY value generated for this table is (as you can see) 5 and not 7 (like your example).
SELECT * FROM dbo.tblEmployees;
/*
EmployeeID Name Salary G
----------- ------------------------------ ---------------------- -
1 Akmal 5000 0
2 Shakira 6000 1
3 Kiron 7000 2
4 Jamil 5500 0
5 Faul 4800 4
*/
GO
All rows have continuous IDENTITY values: there are no gaps.
Now, for some reasons, somebody deletes all rows from dbo.tblEmployees and also decides to reset (RESEED) the last identity value (which is 5) to 1 (from 5 to 1).
DELETE dbo.tblEmployees;
GO
DBCC CHECKIDENT('dbo.tblEmployees', RESEED, 1);
GO
SELECT SCOPE_IDENTITY() AS [Last IDENTITY #2];
/*
Last IDENTITY #2
----------------
1
*/
GO
Now, the last IDENTITY value is 1 (because of that RESEED 1).
insert into tblEmployees values ('Akmal', 5000, 0)
insert into tblEmployees values ('Shakira', 6000, 1)
insert into tblEmployees values ('Kiron', 7000, 2)
insert into tblEmployees values ('Jamil', 5500, 0)
insert into tblEmployees values ('Faul', 4800, 4)
GO
SELECT * FROM dbo.tblEmployees;
GO
/*
EmployeeID Name Salary Gender
----------- ------------------------------ ---------------------- ------
2 Akmal 5000 0
3 Shakira 6000 1
4 Kiron 7000 2
5 Jamil 5500 0
6 Faul 4800 4
*/
When I insert those rows again, the first generated IDENTITY value is 2 (this time).
Why ? The reason is described in MSDN:
"If no rows have been inserted into the table since the table was created, or if all rows have been removed by using the
TRUNCATE TABLE statement, the first row inserted after you run DBCC CHECKIDENT uses new_reseed_value as the identity.
Otherwise, the next row inserted uses new_reseed_value + the current increment value."
This last formula explains why this time the first IDENTITY value is 2:
new_reseed_value (is 1 - because of RESEED 1) + the current increment value (1 - see Note #2) = 1 + 1 = 2.
Note #4: If you are using TRUNCATE TABLE instead of DELETE then the first row inserted after TRUNCATE TABLE will have the ID = seed value (see Note #2) or new_reseed_value = 1. So, in this case you don't need DBCC(..., RESEED, 1).
The second missing value (a possible explanation):
And where is 6?
DELETE dbo.tblEmployees WHERE EmployeeID = 6
insert into tblEmployees values ('Faul', 4800, 4)
GO
SELECT SCOPE_IDENTITY() AS [Last IDENTITY #3];
/*
Last IDENTITY #3
----------------
7
*/
GO
SELECT * FROM dbo.tblEmployees;
GO
/*
EmployeeID Name Salary Gender
----------- ------------------------------ ---------------------- ------
2 Akmal 5000 0
3 Shakira 6000 1
4 Kiron 7000 2
5 Jamil 5500 0
7 Faul 4800 4
*/
Not sure why my previous answer was voted down and deleted or blocked, but if you want to have an identity column with dependable and specified values then you need to set IDENTITY_INSERT to ON on that column, insert your values (with specific ID values) and then set IDENTITY_INSERT back to OFF.
If you do not care what the values end up being (which is probably the case with an Employees table), then forget about it and learn to live with whatever these values end up being.

Swap unique indexed column values in database

I have a database table and one of the fields (not the primary key) is having a unique index on it. Now I want to swap values under this column for two rows. How could this be done? Two hacks I know are:
Delete both rows and re-insert them.
Update rows with some other value
and swap and then update to actual value.
But I don't want to go for these as they do not seem to be the appropriate solution to the problem.
Could anyone help me out?
The magic word is DEFERRABLE here:
DROP TABLE ztable CASCADE;
CREATE TABLE ztable
( id integer NOT NULL PRIMARY KEY
, payload varchar
);
INSERT INTO ztable(id,payload) VALUES (1,'one' ), (2,'two' ), (3,'three' );
SELECT * FROM ztable;
-- This works, because there is no constraint
UPDATE ztable t1
SET payload=t2.payload
FROM ztable t2
WHERE t1.id IN (2,3)
AND t2.id IN (2,3)
AND t1.id <> t2.id
;
SELECT * FROM ztable;
ALTER TABLE ztable ADD CONSTRAINT OMG_WTF UNIQUE (payload)
DEFERRABLE INITIALLY DEFERRED
;
-- This should also work, because the constraint
-- is deferred until "commit time"
UPDATE ztable t1
SET payload=t2.payload
FROM ztable t2
WHERE t1.id IN (2,3)
AND t2.id IN (2,3)
AND t1.id <> t2.id
;
SELECT * FROM ztable;
RESULT:
DROP TABLE
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "ztable_pkey" for table "ztable"
CREATE TABLE
INSERT 0 3
id | payload
----+---------
1 | one
2 | two
3 | three
(3 rows)
UPDATE 2
id | payload
----+---------
1 | one
2 | three
3 | two
(3 rows)
NOTICE: ALTER TABLE / ADD UNIQUE will create implicit index "omg_wtf" for table "ztable"
ALTER TABLE
UPDATE 2
id | payload
----+---------
1 | one
2 | two
3 | three
(3 rows)
I think you should go for solution 2. There is no 'swap' function in any SQL variant I know of.
If you need to do this regularly, I suggest solution 1, depending on how other parts of the software are using this data. You can have locking issues if you're not careful.
But in short: there is no other solution than the ones you provided.
Further to Andy Irving's answer
this worked for me (on SQL Server 2005) in a similar situation
where I have a composite key and I need to swap a field which is part of the unique constraint.
key: pID, LNUM
rec1: 10, 0
rec2: 10, 1
rec3: 10, 2
and I need to swap LNUM so that the result is
key: pID, LNUM
rec1: 10, 1
rec2: 10, 2
rec3: 10, 0
the SQL needed:
UPDATE DOCDATA
SET LNUM = CASE LNUM
WHEN 0 THEN 1
WHEN 1 THEN 2
WHEN 2 THEN 0
END
WHERE (pID = 10)
AND (LNUM IN (0, 1, 2))
There is another approach that works with SQL Server: use a temp table join to it in your UPDATE statement.
The problem is caused by having two rows with the same value at the same time, but if you update both rows at once (to their new, unique values), there is no constraint violation.
Pseudo-code:
-- setup initial data values:
insert into data_table(id, name) values(1, 'A')
insert into data_table(id, name) values(2, 'B')
-- create temp table that matches live table
select top 0 * into #tmp_data_table from data_table
-- insert records to be swapped
insert into #tmp_data_table(id, name) values(1, 'B')
insert into #tmp_data_table(id, name) values(2, 'A')
-- update both rows at once! No index violations!
update data_table set name = #tmp_data_table.name
from data_table join #tmp_data_table on (data_table.id = #tmp_data_table.id)
Thanks to Rich H for this technique.
- Mark
Assuming you know the PK of the two rows you want to update... This works in SQL Server, can't speak for other products. SQL is (supposed to be) atomic at the statement level:
CREATE TABLE testing
(
cola int NOT NULL,
colb CHAR(1) NOT NULL
);
CREATE UNIQUE INDEX UIX_testing_a ON testing(colb);
INSERT INTO testing VALUES (1, 'b');
INSERT INTO testing VALUES (2, 'a');
SELECT * FROM testing;
UPDATE testing
SET colb = CASE cola WHEN 1 THEN 'a'
WHEN 2 THEN 'b'
END
WHERE cola IN (1,2);
SELECT * FROM testing;
so you will go from:
cola colb
------------
1 b
2 a
to:
cola colb
------------
1 a
2 b
I also think that #2 is the best bet, though I would be sure to wrap it in a transaction in case something goes wrong mid-update.
An alternative (since you asked) to updating the Unique Index values with different values would be to update all of the other values in the rows to that of the other row. Doing this means that you could leave the Unique Index values alone, and in the end, you end up with the data that you want. Be careful though, in case some other table references this table in a Foreign Key relationship, that all of the relationships in the DB remain intact.
I have the same problem. Here's my proposed approach in PostgreSQL. In my case, my unique index is a sequence value, defining an explicit user-order on my rows. The user will shuffle rows around in a web-app, then submit the changes.
I'm planning to add a "before" trigger. In that trigger, whenever my unique index value is updated, I will look to see if any other row already holds my new value. If so, I will give them my old value, and effectively steal the value off them.
I'm hoping that PostgreSQL will allow me to do this shuffle in the before trigger.
I'll post back and let you know my mileage.
In SQL Server, the MERGE statement can update rows that would normally break a UNIQUE KEY/INDEX. (Just tested this because I was curious.)
However, you'd have to use a temp table/variable to supply MERGE w/ the necessary rows.
For Oracle there is an option, DEFERRED, but you have to add it to your constraint.
SET CONSTRAINT emp_no_fk_par DEFERRED;
To defer ALL constraints that are deferrable during the entire session, you can use the ALTER SESSION SET constraints=DEFERRED statement.
Source
I usually think of a value that absolutely no index in my table could have. Usually - for unique column values - it's really easy. For example, for values of column 'position' (information about the order of several elements) it's 0.
Then you can copy value A to a variable, update it with value B and then set value B from your variable. Two queries, I know no better solution though.
Oracle has deferred integrity checking which solves exactly this, but it is not available in either SQL Server or MySQL.
1) switch the ids for name
id student
1 Abbot
2 Doris
3 Emerson
4 Green
5 Jeames
For the sample input, the output is:
id student
1 Doris
2 Abbot
3 Green
4 Emerson
5 Jeames
"in case n number of rows how will manage......"