I am creating sequence in SQL Server with the following code. But it displays error as unknown object type. Please give a solution
Here's my code :
create sequence seqval start with 100 increment by 1 minvalue 0 maxvalue 0 no cycle
no cache;
thanks in advance
You can do this.
--Create a dummy TABLE to generate a SEQUENCE. No actual records will be stored.
CREATE TABLE SequenceTABLE
(
ID BIGINT IDENTITY
);
GO
--This procedure is for convenience in retrieving a sequence.
CREATE PROCEDURE dbo.GetSEQUENCE ( #value BIGINT OUTPUT)
AS
--Act like we are INSERTing a row to increment the IDENTITY
BEGIN TRANSACTION;
INSERT SequenceTABLE WITH (TABLOCKX) DEFAULT VALUES;
ROLLBACK TRANSACTION;
--Return the latest IDENTITY value.
SELECT #value = SCOPE_IDENTITY();
GO
--Example execution
DECLARE #value BIGINT;
EXECUTE dbo.GetSEQUENCE #value OUTPUT;
SELECT #value AS [#value];
GO
Create a Numbers table; here's a SO question on the subject. Let's call it dbo.Number.
Have a table with an identity column. Set the seed and step to whatever is appropriate:
create table dbo.SequenceGenerator(ID int identity(1, 1), dummy int);
Then insert values from the numbers table and capture the newly-generated identity values:
declare #HowMany int = 3; -- This determines how large a sequence you receive
-- at each itteration
declare #NewSequenceValue table (ID int);
insert dbo.SequenceGenerator(dummy)
output INSERTED.ID
into #NewSequenceValue
select Number from dbo.Numbers
where Number <= #HowMany;
select * from #NewSequenceValue;
Be sure to DELETE .. dbo.SequenceGenerator from time to time, else it will get big for no additional value. Do not TRUNCATE it - that will reset the IDENTITY column to its initally-declared seed value.
SQL Server 2008 can't create sequences, Sequence objects apply to SQL Server 2012 through current versions.
https://msdn.microsoft.com/es-es/library/ff878091(v=sql.120).aspx
You can use an IDENTITY in your table instead, for example:
CREATE TABLE Person(
Id int IDENTITY(1,1) NOT NULL PRIMARY KEY,
Name varchar(255) NOT NULL
);
The starting value for IDENTITY is 1, and it will increment by 1 for each new record.
http://www.w3schools.com/sql/sql_autoincrement.asp
WITH N0 as (SELECT 1 as n UNION ALL SELECT 1)
,N1 as (SELECT 1 as n FROM N0 t1, N0 t2)
,N2 as (SELECT 1 as n FROM N1 t1, N1 t2)
,N3 as (SELECT 1 as n FROM N2 t1, N2 t2)
,nums as (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) as num FROM N3)
SELECT * FROM nums
We can't use Sequence easily in SQL Server 2008.
You can use CTE(Common Table Expressions) for Sequence Generation in SQL Server 2008
WITH NUM_GEN (n) AS
(
SELECT 1
UNION
ALLSELECT n+1
FROM NUM_GEN
WHERE n+1< MAX_VALUE
)
SELECT n
FROM NUM_GEN
Are you sure you're running 2012? I had no trouble with:
CREATE SEQUENCE seqval
START WITH 100
INCREMENT BY 1
minvalue 100 maxvalue 10000 NO CYCLE
Your 0,0 values generated a syntax error for me but a clear and simple one.
The minimum value for sequence object 'seqval' must be less than its maximum value.
Related
For example, there is a table
int type
int number
int value
How to make that when inserting a value into a table
indexing started from 1 for different types.
type 1 => number 1,2,3...
type 2 => number 1,2,3...
That is, it will look like this.
type
number
value
1
1
-
1
2
-
1
3
-
2
1
-
1
4
-
2
2
-
3
1
-
6
1
-
1
5
-
2
3
-
6
2
-
Special thanks to #Larnu.
As a result, in my case, the best solution would be to create a table for each type.
As I mentioned in the comments, neither IDENTITY nor SEQUENCE support the use of another column to denote what "identity set" they should use. You can have multiple SEQUENCEs which you could use for a single table, however, this doesn't scale. If you are specific limited to 2 or 3 types, for example, you might choose to create 3 SEQUENCE objects, and then use a stored procedure to handle your INSERT statements. Then, when a user/application wants to INSERT data, they call the procedure and that procedure has logic to use the SEQUENCE based on the value of the parameter for the type column.
As mentioned, however, this doesn't scale well. If you have an undeterminate number of values of type then you can't easily handle getting the right SEQUENCE and handling new values for type would be difficult too. In this case, you would be better off using a IDENTITY and then a VIEW. The VIEW will use ROW_NUMBER to create your identifier, while IDENTITY gives you your always incrementing value.
CREATE TABLE dbo.YourTable (id int IDENTITY(1,1),
[type] int NOT NULL,
number int NULL,
[value] int NOT NULL);
GO
CREATE VIEW dbo.YourTableView AS
SELECT ROW_NUMBER() OVER (PARTITION BY [type] ORDER BY id ASC) AS Identifier,
[type],
number,
[value]
FROM dbo.YourTable;
Then, instead, you query the VIEW, not the TABLE.
If you need consistency of the column (I name identifier) you'll need to also ensure row(s) can't be DELETEd from the table. Most likely by adding an IsDeleted column to the table defined as a bit (with 0 for no deleted, and 1 for deleted), and then you can filter to those rows in the VIEW:
CREATE VIEW dbo.YourTableView AS
WITH CTE AS(
SELECT id,
ROW_NUMBER() OVER (PARTITION BY [type] ORDER BY id ASC) AS Identifier,
[type],
number,
[value],
IsDeleted
FROM dbo.YourTable)
SELECT id,
Identifier,
[type],
number,
[value]
FROM CTE
WHERE IsDeleted = 0;
You could, if you wanted, even handle the DELETEs on the VIEW (the INSERT and UPDATEs would be handled implicitly, as it's an updatable VIEW):
CREATE TRIGGER trg_YourTableView_Delete ON dbo.YourTableView
INSTEAD OF DELETE AS
BEGIN
SET NOCOUNT ON;
UPDATE YT
SET IsDeleted = 1
FROM dbo.YourTable YT
JOIN deleted d ON d.id = YT.id;
END;
GO
db<>fiddle
For completion, if you wanted to use different SEQUENCE object, it would look like this. Notice that this does not scale easily. I have to CREATE a SEQUENCE for every value of Type. As such, for a small, and known, range of values this would be a solution, but if you are going to end up with more value for type or already have a large range, this ends up not being feasible pretty quickly:
CREATE TABLE dbo.YourTable (identifier int NOT NULL,
[type] int NOT NULL,
number int NULL,
[value] int NOT NULL);
CREATE SEQUENCE dbo.YourTable_Type1
START WITH 1 INCREMENT BY 1;
CREATE SEQUENCE dbo.YourTable_Type2
START WITH 1 INCREMENT BY 1;
CREATE SEQUENCE dbo.YourTable_Type3
START WITH 1 INCREMENT BY 1;
GO
CREATE PROC dbo.Insert_YourTable #Type int, #Number int = NULL, #Value int AS
BEGIN
DECLARE #Identifier int;
IF #Type = 1
SELECT #Identifier = NEXT VALUE FOR dbo.YourTable_Type1;
IF #Type = 2
SELECT #Identifier = NEXT VALUE FOR dbo.YourTable_Type2;
IF #Type = 3
SELECT #Identifier = NEXT VALUE FOR dbo.YourTable_Type3;
INSERT INTO dbo.YourTable (identifier,[type],number,[value])
VALUES(#Identifier, #Type, #Number, #Value);
END;
I have the data ready to Insert into my Production table however the ID column is NULL and that needs to be pre-populated with the IDs prior to Insert. I have these IDs in another Temp Table... all I want is to simply apply these IDs to the records in my Temp Table.
For example... Say I have 10 records all simply needing IDs. I have in another temp table exactly 10 IDs... they simply need to be applied to my 10 records in my 'Ready to INSERT' Temp Table.
I worked in Oracle for about 9 years and I would have done this simply by looping over my 'Collection' using a FORALL Loop... basically I would simply loop over my 'Ready to INSERT' temp table and for each row apply the ID from my other 'Collection'... in SQL Server I'm working with Temp Tables NOT Collections and well... there's no FORALL Loop or really any fancy loops in SQL Server other than WHILE.
My goal is to know the appropriate method to accomplish this in SQL Server. I have learned that in the SQL Server world so many of the DML operations are all SET Based whereas when I worked in oracle we handled data via arrays/collections and using CURSORS or LOOPs we would simply iterate thru the data. I've seen in the SQL Server world using CURSORS and/or iterating thru data record by record is frowned upon.
Help me get my head out of the 'Oracle' space I was in for so long and into the 'SQL Server' space I need to be in. This has been a slight struggle.
The code below is how I've currently implemented this however it just seems convoluted.
SET NOCOUNT ON;
DECLARE #KeyValueNewMAX INT,
#KeyValueINuse INT,
#ClientID INT,
#Count INT;
DROP TABLE IF EXISTS #InterOtherSourceData;
DROP TABLE IF EXISTS #InterOtherActual;
DROP TABLE IF EXISTS #InterOtherIDs;
CREATE TABLE #InterOtherSourceData -- Data stored here for DML until data is ready for INSERT
(
UniqueID INT IDENTITY( 1, 1 ),
NewIntOtherID INT,
ClientID INT
);
CREATE TABLE #InterOtherActual -- Prod Table where the data will be INSERTED Into
(
IntOtherID INT,
ClientID INT
);
CREATE TABLE #InterOtherIDs -- Store IDs needing to be applied to Data
(
UniqueID INT IDENTITY( 1, 1 ),
NewIntOtherID INT
);
BEGIN
/* TEST Create Fake Data and store it in temp table */
WITH fakeIntOtherRecs AS
(
SELECT 1001 AS ClientID, 'Jake' AS fName, 'Jilly' AS lName UNION ALL
SELECT 2002 AS ClientID, 'Jason' AS fName, 'Bateman' AS lName UNION ALL
SELECT 3003 AS ClientID, 'Brain' AS fName, 'Man' AS lName
)
INSERT INTO #InterOtherSourceData (ClientID)
SELECT fc.ClientID--, fc.fName, fc.lName
FROM fakeIntOtherRecs fc
;
/* END TEST Prep Fake Data */
/* Obtain count so we know how many IDs we need to create */
SELECT #Count = COUNT(*) FROM #InterOtherSourceData;
PRINT 'Count: ' + CAST(#Count AS VARCHAR);
/* For testing set value OF KeyValuePre to the max key currently in use by Table */
SELECT #KeyValueINuse = 13;
/* Using the #Count let's obtain the new MAX ID... basically Existing_Key + SourceRecordCount = New_MaxKey */
SELECT #KeyValueNewMAX = #KeyValueINuse + #Count /* STORE new MAX ID in variable */
/* Print both keys for testing purposes to review */
PRINT 'KeyValue Current: ' + CAST(#KeyValueINuse AS VARCHAR) + ' KeyValue Max: ' + CAST(#KeyValueNewMAX AS VARCHAR);
/* Using recursive CTE generate a fake table containing all of the IDs we want to INSERT into Prod Table */
WITH CTE AS
(
SELECT (#KeyValueNewMAX - #Count) + 1 AS STARTMINID, #KeyValueNewMAX AS ENDMAXID UNION ALL
/* SELECT FROM CTE to create Recursion */
SELECT STARTMINID + 1 AS STARTMINID, ENDMAXID FROM CTE
WHERE (STARTMINID + 1) < (#KeyValueNewMAX + 1)
)
INSERT INTO #InterOtherIDs (NewIntOtherID)
SELECT c.STARTMINID AS NewIntOtherID
FROM CTE c
;
/* Apply New IDs : Using the IDENTITY fields on both Temp Tables I can JOIN the tables by the IDENTITY columns
| Is there a BETTER Way to do this?... like LOOP over each record rather than having to build up common IDs in both tables using IDENTITY columns?
*/
UPDATE #InterOtherSourceData SET NewIntOtherID = oi.NewIntOtherID
FROM #InterOtherIDs oi
JOIN #InterOtherSourceData o ON o.UniqueID = oi.UniqueID
;
/* View data that is ready for insert */
--SELECT *
--FROM #InterOtherSourceData
--;
/* INSERT DATA INTO PRODUCTION TABLE */
INSERT INTO #InterOtherActual (IntOtherID, ClientId)
SELECT NewIntOtherID, ClientID
FROM #InterOtherSourceData
;
SELECT * FROM #InterOtherActual;
END
To pre-generate key values in SQL Server use a sequence rather than an IDENTITY column.
eg
drop table if exists t
drop table if exists #t_stg
drop sequence t_seq
go
create sequence t_seq start with 1 increment by 1
create table t(id int primary key default (next value for t_seq),a int, b int)
create table #t_stg(id int, a int, b int)
insert into #t_stg(a,b) values (1,2),(3,3),(4,5)
update #t_stg set id = next value for t_seq
--select * from #t_stg
insert into t(id,a,b)
select * from #t_stg
I am moving a small database from MS Access into SQL Server. Each year, the users would create a new Access database and have clean data, but this change will put data across the years into one pot. The users have relied on the autonumber value in Access as a reference for records. That is very inaccurate if, say, 238 records are removed.
So I am trying to accommodate them with an id column they can control (somewhat). They will not see the real primary key in the SQL table, but I want to give them an ID they can edit, but still be unique.
I've been working with this trigger, but it has taken much longer than I expected.
Everything SEEMS TO work fine, except I don't understand why I have the same data in my INSERTED table as the table the trigger is on. (See note in code.)
ALTER TRIGGER [dbo].[trg_tblAppData]
ON [dbo].[tblAppData]
AFTER INSERT,UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE #NewUserEnteredId int = 0;
DECLARE #RowIdForUpdate int = 0;
DECLARE #CurrentUserEnteredId int = 0;
DECLARE #LoopCount int = 0;
--*** Loop through all records to be updated because the values will be incremented.
WHILE (1 = 1)
BEGIN
SET #LoopCount = #LoopCount + 1;
IF (#LoopCount > (SELECT Count(*) FROM INSERTED))
BREAK;
SELECT TOP 1 #RowIdForUpdate = ID, #CurrentUserEnteredId = UserEnteredId FROM INSERTED WHERE ID > #RowIdForUpdate ORDER BY ID DESC;
IF (#RowIdForUpdate IS NULL)
BREAK;
-- WHY IS THERE A MATCH HERE? HAS THE RECORD ALREADY BEEN INSERTED?
IF EXISTS (SELECT UserEnteredId FROM tblAppData WHERE UserEnteredId = #CurrentUserEnteredId)
BEGIN
SET #NewUserEnteredId = (SELECT Max(t1.UserEnteredId) + 1 FROM tblAppData t1);
END
ELSE
SET #NewUserEnteredId = #CurrentUserEnteredId;
UPDATE tblAppData
SET UserEnteredId = #NewUserEnteredId
FROM tblAppData a
WHERE a.ID = #RowIdForUpdate
END
END
Here is what I want to accomplish:
When new record(s) are added, it should increment values from the Max existing
When a user overrides a value, it should check to see the existence of that value. If found restore the existing value, otherwise allow the change.
This trigger allows for multiple rows being added at a time.
It is great for this to be efficient for future use, but in reality, they will only add 1,000 records a year.
I wouldn't use a trigger to accomplish this.
Here is a script you can use to create a sequence (op didn't tag version), create the primary key, use the sequence as your special id, and put a constraint on the column.
create table dbo.test (
testid int identity(1,1) not null primary key clustered
, myid int null constraint UQ_ unique
, somevalue nvarchar(255) null
);
create sequence dbo.myid
as int
start with 1
increment by 1;
alter table dbo.test
add default next value for dbo.myid for myid;
insert into dbo.test (somevalue)
select 'this' union all
select 'that' union all
select 'and' union all
select 'this';
insert into dbo.test (myid, somevalue)
select 33, 'oops';
select *
from dbo.test
insert into dbo.test (somevalue)
select 'oh the fun';
select *
from dbo.test
--| This should error
insert into dbo.test (myid, somevalue)
select 3, 'This is NO fun';
Here is the result set:
testid myid somevalue
1 1 this
2 2 that
3 3 and
4 4 this
5 33 oops
6 5 oh the fun
And at the very end a test, which will error.
I have a material table as source table as follows :
CREATE TABLE dbo.MATERIAL (
ID int IDENTITY(1,1) NOT NULL,
CATEGORY_ID int NOT NULL,
SECTION_ID int NOT NULL,
STATUS_ID int NOT NULL
)
also I have a Order table as the target table as follows :
CREATE TABLE dbo.ORDER (
ID int IDENTITY(1,1) NOT NULL,
MATERIAL_ID int NOT NULL
)
I am sending ome data to SQL server to a Stored Procedure and have the following Temp Table created
DECLARE #temptable TABLE (
CATEGORY_ID int NOT NULL,
SECTION_ID int NOT NULL,
STATUS_ID int NOT NULL,
COUNT int NOT NULL
)
and had it filled with data as follows :
CATEGORY_ID SECTION_ID STATUS_ID COUNT
----------- ---------- --------- -----
3 8 1 10
8 2 2 11
4 6 1 8
What I want is to match COUNT number of materials from MATERIAL table which matches ith the given CATEGORY_ID, SECTION_ID and STATUS_ID triple of the same row; then insert those records's IDs to the target table, ORDER.
How can I acomplish this task?
Regards.
I think I have found the solution. I should have used CURSOR. The following code does the trick :
DECLARE #MATERIAL_ID int
DECLARE #CATEGORY_ID int
DECLARE #SECTION_ID int
DECLARE #STATUS_ID int
DECLARE #COUNT int
DECLARE cur CURSOR LOCAL FOR
SELECT
CATEGORY_ID,
SECTION_ID,
STATUS_ID,
COUNT
FROM
#temptable
OPEN cur
FETCH NEXT FROM cur INTO #CATEGORY_ID, #SECTION_ID, #STATUS_ID, #COUNT
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO
dbo.ORDER
(MATERIAL_ID)
SELECT
TOP (#COUNT) ID
FROM
dbo.MATERIAL AS m
WHERE
m.CATEGORY_ID = #CATEGORY_ID
AND m.SECTION_ID = #SECTION_ID
AND m.STATUS_ID = #STATUS_ID
FETCH NEXT FROM cur INTO #CATEGORY_ID, #SECTION_ID, #STATUS_ID, #COUNT
END
CLOSE cur
DEALLOCATE cur
Threw away a bunch of previous work now that I think I understand requirement.
Here's a working SQL FIDDLE:
This generates a data set called GENROWS that contains rows equal to the count of the max count in temptable. It does this by using a recursive Common table expression (CTE) to generate 1 row for each count of the max count in temptable.
It then uses this data set to join to temptable and material to generate the # of times the material needs to be inserted in order.
I'm not a big fan of using reserved words so I adjusted order to morder and I would recommend adjusting column count otherwise you'll be stuck wrapping words in []'s from time to time.
NOTE: This assumes there will not be duplicates (records with same category_ID, Section_Id and Status_ID) in material table. If there are; then this may or may not behave as expected.
And lastly now that I have a better understanding of what you're after I'm not positive you'll see much of a performance gain compared to using cursors. As the rows have to be generated somehow. This still may work a bit faster because we generate the set and insert all at once as opposed to individually. But there's overhead with producing storing and retrieving the data set produced which may offset this gain. Only testing would tell.
WITH
GenRows (RowNumber, Val) AS (
-- Anchor member definition
SELECT 1 AS RowNumber, (Select max(count) val from temptable) val
UNION ALL
-- Recursive member definition
SELECT a.RowNumber + 1 AS RowNumber, a.val
FROM GenRows a
WHERE a.RowNumber < a.val
)
Insert into morder (Material_ID)
SELECT A.ID
FROM material A
INNER JOIN temptable B
on A.Category_ID = B.Category_ID
and A.Section_Id = B.Section_Id
and A.Status_Id = B.Status_ID
INNER JOIN GenRows
on GenRows.RowNumber <= b.[count]
My Table structure is
id type no amount
1 type1 a1 1000
2 type1 a2 2000
3 type2 b1 3000
4 type3 c1 4000
5 type1 a3 5000
6 type2 b2 6000
7 type2 b3 7000
8 type3 c2 8000
now i wants to increment the no field data based on the type.
for example for type1 the next no is a4
and
for numeric only I am using the following code
SELECT ISNULL(Max(No),0)+1 AS No FROM table
but how to do it for with Alphabets in SQL Server 2005
Assuming that prefixes are of single character length, you may try following:
;with cte as (
select type, typePrefix = left(no, 1), typeNum = right(no, len(no) - 1)
from TableName
)
select typePrefix + cast(isnull(max(typeNum), 0) + 1 as varchar(10))
from cte
where type = 'type1'
group by typePrefix
But it will not work if you try to generate next no for a type which is not in table (e.g. 'type4'). To allow it, you may need a separate table, where prefix for each type is specified:
create table TypePrefixes (type varchar(50), prefix varchar(10))
insert into TypePrefixes values ('type1', 'a')
insert into TypePrefixes values ('type2', 'b')
insert into TypePrefixes values ('type3', 'c')
insert into TypePrefixes values ('another_type', 'd')
--etc.
In this case, statement to get next no will look as:
select tp.prefix + cast(isnull(max(cast(right(t.no, len(t.no) - len(tp.prefix)) as int)), 0) + 1 as varchar(20))
from TableName t
right join TypePrefixes tp on tp.type = t.type
where tp.type = 'type4'
group by tp.prefix
Also, you may just wish to calculate no for each record on the fly, like:
;with cte as (
select *,
typeNum = row_number() over (partition by type order by id),
typePrefix = char(dense_rank() over (order by type) + ascii('a') - 1)
from TableName
)
select *, No2 = typePrefix + cast(typeNum as varchar(10))
from cte
However, the latter is limited in number of distinct types in your table, which should not exceed 26 (so that we not go beyond 'z').
try something like
SELECT ISNULL(Max(No),0)+1 AS No FROM table group by type
First, you need an UNIQUE index on No column:
CREATE UNIQUE INDEX IUN_MyTable_On
ON MySchema.MyTable(On);
GO
This unique index will prevent duplicate values but, also, will help the query below.
Second, you could use this script to generate the next No for a given letter:
DECLARE #Chr CHAR(1);
SET #Chr='A';
BEGIN TRY
BEGIN TRANSACTION;
DECLARE #LastId INT;
DECLARE #NewNo VARCHAR(...); -- Fill with No's max. length
-- Previous index will help this query
SELECT #LastId=MAX( CONVERT(INT,SUBSTRING(#LastNo,2,8000)) )
FROM MySchema.MyTable x WITH(UPDLOCK) -- It locks the rows to prevent a concurent session to generate the same value (No)
WHERE x.No LIKE #Chr+'%';
SET #NewNo=#Chr+CONVERT(VARCHAR(11),ISNULL(#LastId,0)+1);
-- Do whatever you want with the new value: ex. INSERT
INSERT INTO ... (No,...)
VALUES (#NewNo,...);
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
DECLARE #ErrMsg NVARCHAR(2000);
SET #ErrMsg=ERROR_MESSAGE();
IF ##TRANCOUNT>0
BEGIN
ROLLBACK;
END
RAISERROR(#ErrMsg,16,1);
END CATCH
Note #1: This solution should be safe if this is the only way to generate the new values (#NewNo).
Note #2: If that SELECT query acquires at least 5000 locks then SQL Server will escalate locks at table/partition level.