Reset A Sequence - sql-server

I've read several posts about the TSQL Identity Bug and have been playing around with using SEQUENCE. However, I'm curious about resetting the SEQUENCE on the ID value in a table. For an example:
CREATE SEQUENCE Inc
AS INT
START WITH 1
INCREMENT BY 1
CYCLE
CACHE
-- Quick ability to redo everything if needed:
-- DROP SEQUENCE Inc
-- Our table grabs the next sequence for our ID field:
CREATE TABLE SequenceID(
NewIDField INT DEFAULT NEXT VALUE FOR Inc,
Name VARCHAR(100)
)
INSERT INTO SequenceID (Name)
VALUES ('John')
, ('Tiffany')
, ('Bob')
, ('Jessica')
SELECT *
FROM SequenceID
-- We remove Bob:
DELETE FROM SequenceID
WHERE NewIDField = 3
-- ID value 3 is gone; it moves from 1 to 2 to 4
SELECT *
FROM SequenceID
INSERT INTO SequenceID (Name)
VALUES ('David')
, ('Rosa')
, ('Samuel')
-- ID 3 doesn't exist because the SEQUENCE grabs the next value from 4
SELECT *
FROM SequenceID
-- Let's just reset our ID
;WITH ResetIt AS(
SELECT ROW_NUMBER() OVER (ORDER BY NewIDField) AS ID
, NewIDField AS ExistingID
, Name
FROM SequenceID
)
UPDATE SequenceID
SET NewIDField = ResetIt.ID
FROM ResetIt
WHERE SequenceID.NewIDField = ResetIt.ExistingID
-- Yay!
SELECT *
FROM SequenceID
INSERT INTO SequenceID (Name)
VALUES ('Sarah')
-- Oh Sarah, tsk tsk.
SELECT *
FROM SequenceID
DROP TABLE SequenceID
Is there a way to automatically perform this with SEQUENCE where we can determine the last value and begin there (similar to a RESEED), as even with IDENTITY, if we remove a value, we still must RESEED, see:
CREATE TABLE IDID(
ID INT IDENTITY(1,1),
I INT
)
INSERT INTO IDID (I)
VALUES (1),(2),(3),(4)
SELECT *
FROM IDID
DELETE FROM IDID
WHERE ID = 3
INSERT INTO IDID (I)
VALUES (5),(6),(7)
SELECT *
FROM IDID
DROP TABLE IDID

After you perform your update, you'll have to run some dynamic SQL, as ALTER SEQUENCE only accepts a constant for the RESTART WITH clause:
DECLARE #resetSQL nvarchar(255) = 'ALTER SEQUENCE Inc RESTART WITH ' + (SELECT CAST(MAX(NewIDField)+1 as nvarchar(10)) FROM SequenceID);
exec sp_executesql #resetSQL;

Related

Issue with delete statement in merge statement

I am working with merge into statement
I have a table that looks like this (first I insert like this) using below query:
5
2
5
3
5
5
5
6
table type :
CREATE TYPE [dbo].[userid] AS TABLE(
[userid] [bigint] NULL
)
GO
Now I want the below output :
5
2
5
3
5
6
I write the below query like this:
--use test
declare #sid varchar(100) = '5'
declare #uid as userid
insert into #uid(userid) values(2)
insert into #uid(userid) values(3)
--insert into #uid(userid) values(5) // I remove this line
insert into #uid(userid) values(6)
MERGE INTO dbo.test_master AS dest
USING #uid AS sou ON
dest.sid = #sid
AND
sou.userid = dest.testid
WHEN MATCHED THEN UPDATE SET
dest.testid = sou.userid
WHEN NOT MATCHED THEN
INSERT( sid, testid )
VALUES( #sid, sou.userid )
--WHEN NOT MATCHED BY SOURCE
-- THEN
-- DELETE
;
I am trying to achieve this output
5
2
5
3
5
6
I am using delete keyword, see my SQL query, but it is deleting the all records from the table. I try, but can't work it out.
You need to pre-filter the destination table, otherwise all rows, even ones that have a different sid will be deleted. You can pre-filter with a CTE or a view.
I note that the WHEN MATCHED clause makes no sense, as the only column being updated is the join column, which obviously matches anyway.
declare #sid varchar(100) = '5';
declare #uid as userid;
insert into #uid (userid) values
(2),
(3),
-- (5), -- I remove this line
(6);
WITH dest AS (
SELECT *
FROM dbo.test_master dest
WHERE dest.sid = #sid
)
MERGE INTO dest
USING #uid AS sou ON
sou.userid = dest.testid
-- WHEN MATCHED THEN UPDATE SET
-- dest.testid = sou.userid -- doesn't make sense
WHEN NOT MATCHED THEN
INSERT( sid, testid )
VALUES( #sid, sou.userid )
WHEN NOT MATCHED BY SOURCE THEN
DELETE
;
db<>fiddle

SQL Server trigger on Insert and Update

I am looking to create a SQL Server trigger that moves a record from one table to an identical replica table if the record matches a specific condition.
Questions: do I need to specify each column, or can I use a wildcard?
Can I use something like:
SET #RecID = (SELECT [RecoID] FROM Inserted)
IF NULLIF(#RecID, '') IS NOT NULL
(then insert....)
THANKS!
There's a lot of stuff you "CAN" do in a trigger, but that doesn't mean you should. I'd would urge to to avoid setting scalar variables within a trigger at all costs. Even if you 100% sure your table will never have more that 1 row inserted per transaction because that's how the app is designed... You'll be in for very rude awakening when you find out that not all transactions come through the application.
Below is a quick demonstration of both types of triggers...
USE tempdb;
GO
IF OBJECT_ID('tempdb.dbo.PrimaryTable', 'U') IS NOT NULL
DROP TABLE dbo.PrimaryTable;
GO
IF OBJECT_ID('tempdb.dbo.TriggerScalarLog', 'U') IS NOT NULL
DROP TABLE dbo.TriggerScalarLog;
GO
IF OBJECT_ID('tempdb.dbo.TriggerMultiRowLog', 'U') IS NOT NULL
DROP TABLE dbo.TriggerMultiRowLog;
GO
CREATE TABLE dbo.PrimaryTable (
Pt_ID INT NOT NULL IDENTITY (1,1) PRIMARY KEY CLUSTERED,
Col_1 INT NULL,
Col_2 DATE NOT NULL
CONSTRAINT df_Col2 DEFAULT (GETDATE())
);
GO
CREATE TABLE dbo.TriggerScalarLog (
Pt_ID INT,
Col1_Old INT,
Col1_New INT,
Col2_Old DATE,
Col2_New DATE
);
GO
CREATE TABLE dbo.TriggerMultiRowLog (
Pt_ID INT,
Col1_Old INT,
Col1_New INT,
Col2_Old DATE,
Col2_New DATE
);
GO
--=======================================================
CREATE TRIGGER dbo.PrimaryCrudScalar ON dbo.PrimaryTable
AFTER INSERT, UPDATE, DELETE
AS
SET NOCOUNT ON;
DECLARE
#Pt_ID INT,
#Col1_Old INT,
#Col1_New INT,
#Col2_Old DATE,
#Col2_New DATE;
SELECT
#Pt_ID = ISNULL(i.Pt_ID, d.Pt_ID),
#Col1_Old = d.Col_1,
#Col1_New = i.Col_1,
#Col2_Old = d.Col_2,
#Col2_New = i.Col_2
FROM
Inserted i
FULL JOIN Deleted d
ON i.Pt_ID = d.Pt_ID;
INSERT dbo.TriggerScalarLog (Pt_ID, Col1_Old, Col1_New, Col2_Old, Col2_New)
VALUES (#Pt_ID, #Col1_Old, #Col1_New, #Col2_Old, #Col2_New);
GO -- DROP TRIGGER dbo.PrimaryCrudScalar;
CREATE TRIGGER PrimaryCrudMultiRow ON dbo.PrimaryTable
AFTER INSERT, UPDATE, DELETE
AS
SET NOCOUNT ON;
INSERT dbo.TriggerMultiRowLog (Pt_ID, Col1_Old, Col1_New, Col2_Old, Col2_New)
SELECT
ISNULL(i.Pt_ID, d.Pt_ID),
d.Col_1,
i.Col_1,
d.Col_2,
i.Col_2
FROM
Inserted i
FULL JOIN Deleted d
ON i.Pt_ID = d.Pt_ID;
GO -- DROP TRIGGER dbo.TriggerMultiRowLog;
--=======================================================
--=======================================================
-- --insert test...
INSERT dbo.PrimaryTable (Col_1)
SELECT TOP 100
o.object_id
FROM
sys.objects o;
SELECT 'INSERT Scarar results';
SELECT * FROM dbo.TriggerScalarLog tsl;
SELECT 'INSERT Multi-Row results';
SELECT * FROM dbo.TriggerMultiRowLog tmrl;
UPDATE pt SET
pt.Col_1 = pt.Col_1 + rv.RandomVal,
pt.Col_2 = DATEADD(DAY, rv.RandomVal, pt.Col_2)
FROM
dbo.PrimaryTable pt
CROSS APPLY ( VALUES (ABS(CHECKSUM(NEWID())) % 10000 + 1) ) rv (RandomVal);
SELECT 'UPDATE Scarar results';
SELECT * FROM dbo.TriggerScalarLog tsl;
SELECT 'UPDATE Multi-Row results';
SELECT * FROM dbo.TriggerMultiRowLog tmrl;
DELETE pt
FROM
dbo.PrimaryTable pt;
SELECT 'DELETE Scarar results';
SELECT * FROM dbo.TriggerScalarLog tsl;
SELECT 'DELETE Multi-Row results';
SELECT * FROM dbo.TriggerMultiRowLog tmrl;
You could, but I'd recommend against it. If your source table changed things would start failing.
Also, in your example if you were to ever have more than one row inserted at a time you would get thrown an error (or have unpredictable results). I'd recommend a more set based approach:
INSERT table2 ( user_id ,
user_name ,
RecoID
)
SELECT user_id ,
user_name ,
RecoID
FROM inserted i
LEFT JOIN table2 t ON i.RecoID = t.RecoID
WHERE t.RecoID IS NULL;
EDIT:
If you want to stop the insert happening on your original table then you'll need to do something along the lines of:
CREATE TRIGGER trigger_name
ON table_orig
INSTEAD OF INSERT
AS
BEGIN
-- make sure we aren't triggering from ourselves from another trigger
IF TRIGGER_NESTLEVEL() <= 1
return;
-- insert into the table_copy if the inserted row is already in table_orig (not null)
INSERT table_copy ( user_id ,
user_name ,
RecoID
)
SELECT user_id ,
user_name ,
RecoID
FROM inserted i
LEFT JOIN table_orig c ON i.RecoID = c.RecoID
WHERE t.RecoID IS NOT NULL;
-- insert into table_orig if the inserted row is not already in table_orig (null)
INSERT table_orig ( user_id ,
user_name ,
RecoID
)
SELECT user_id ,
user_name ,
RecoID
FROM inserted i
LEFT JOIN table_orig c ON i.RecoID = c.RecoID
WHERE t.RecoID IS NULL;
END;
The instead of will stop the insert if you don't want it to actually be inserted, so you'll need to do that yourself (the second insert statement).
Please note I changed some nulls to not nulls and the table we are left joining to in some cases.

Is multi-row uniqueness possible in SQL Server?

I have a table where I try to define a covariate gouping like so
ID Rank Covariate
1 1 Age
1 2 Gender
1 3 YearOfBirth
2 1 Gender
The ID captures which covariates belong together in the same group. So covariate group 1 (ID = 1) is composed of age, gender and year of birth, whereas group 2 is Gender only.
Now, inserting a new covariate group consisting of, say gender only, should be illegal as this group already exists, however inserting a new group consisting of Age and Gender should be allowed (it is a subset of group 1 but not an exact match).
Also the rank matters so
ID Rank Covariate
2 Age
1 Gender
3 YearOfBirth
Should not be considered equal to group 1.
Is there a way to enforce this in sql-server?
Ideally the ID column would autoincrement on a legal insert (but thats a different issue).
I don’t know of any means to enforce the Covariant group uniqueness criteria via standard uniqueness constraints or check constraints or any other elegant solution for that matter. However, you can enforce your constraints by only allowing access to the table via a stored procedure or alternatively a view with a “INSTEAD OF INSERT” trigger defined.
Method 1 - Stored Procedure
The following example shows the stored procedure method. First we create a table value type so that we can pass our covariant group as a read-only parameter to our stored procedure.
CREATE TYPE CovariateGroupEntry AS TABLE
(
[Rank] INT NOT NULL
,[Covariate] NVARCHAR(50)
PRIMARY KEY([Rank], [Covariate])
)
Next we create our base table that will contain our covariant groups:
CREATE TABLE CovariateGroups
(
[ID] INT NOT NULL
,[Rank] INT NOT NULL
,[Covariate] NVARCHAR(50)
PRIMARY KEY([ID], [Rank], [Covariate])
)
Next step we create a dummy table that will be used to auto generate our ID:
CREATE TABLE CovariateGroupIDs
(
[GroupID] INT PRIMARY KEY IDENTITY
,[CreatedDateTime] DATETIME NOT NULL
)
Final step we create our procedure:
CREATE PROCEDURE CovariateGroup_Add
(
#covariateGroupEntry dbo.CovariateGroupEntry READONLY
)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #groupID INT;
DECLARE #groupSize INT;
DECLARE #groupMatchCount INT;
DECLARE #minRank INT;
DECLARE #maxRankDelta INT;
DECLARE #minRankDelta INT;
-- Get the size of the new group which user will attempt to add.
SELECT #groupSize = COUNT([Rank])
FROM #covariateGroupEntry
-- Validate that the new group rank starts at 1 and increments by 1 step value only.
SELECT #minRank = ISNULL(MIN([Rank]), 0)
,#maxRankDelta = ISNULL(MAX(Delta), 0)
,#minRankDelta = ISNULL(MIN(Delta), 0)
FROM (
SELECT [Rank]
,[Rank] - (LAG([Rank], 1, 0) OVER (ORDER BY [Rank])) AS Delta
FROM #covariateGroupEntry
) RankValidation
IF ( (#minRank > 1) OR (#maxRankDelta > 1) OR (#minRankDelta < 1) )
BEGIN
-- Raise an error if our input data sets rank column does not start at 1 or does not increment by 1 as expected.
RAISERROR (N'Attempting to add covariant group with invalid rank order.', -- Message text.
15, -- Severity,
1 -- State
); -- Second argument.
END
ELSE
BEGIN
-- Generate a new group ID
INSERT INTO [dbo].[CovariateGroupIDs]
(
[CreatedDateTime]
)
SELECT GETDATE() AS [CreatedDateTime]
SET #groupID = SCOPE_IDENTITY();
WITH CTE_GroupsCompareSize
AS
(
-- Compare the size of the new group with all of the existing groups. If the size is different we can
-- safely assume that the group is either a sub set or super set of the compared group. These groups
-- can be excluded from further consideration.
SELECT [CovariateGroups].[ID]
,[CovariateGroups].[Rank]
,[CovariateGroups].[Covariate]
,COUNT([CovariateGroups].[Rank]) OVER (PARTITION BY [CovariateGroups].[ID]) GroupSize
,#groupSize AS NewGroupSize
FROM [CovariateGroups]
)
,CTE_GroupsCompareRank
AS
(
-- For groups of the same size left outer join the new group on the original groups on both rank and covariant entry.
-- If the MIN() OVER window function return a value of 0 then there is at least on entry in the compared groups that does
-- not match and is therefore deemed different.
SELECT [OrginalGroup].[ID]
,[OrginalGroup].[Rank]
,[OrginalGroup].[Covariate]
,MIN(
CASE
WHEN [NewGroup].[Covariate] IS NULL THEN 0
ELSE 1
END
) OVER (PARTITION BY [OrginalGroup].[ID]) AS EntireGroupRankMatch
FROM CTE_GroupsCompareSize [OrginalGroup]
LEFT OUTER JOIN #covariateGroupEntry [NewGroup] ON ([OrginalGroup].[Rank] = [NewGroup].[Rank] AND [OrginalGroup].[Covariate] = [NewGroup].[Covariate])
WHERE GroupSize = NewGroupSize
)
SELECT #groupMatchCount = COUNT(EntireGroupRankMatch)
FROM CTE_GroupsCompareRank
WHERE EntireGroupRankMatch = 1
IF ISNULL(#groupMatchCount, 0) = 0
BEGIN
INSERT INTO [CovariateGroups]
(
[ID]
,[Rank]
,[Covariate]
)
SELECT #groupID AS [ID]
,[Rank]
,[Covariate]
FROM #covariateGroupEntry
END
ELSE
BEGIN
-- Raise an error if our uniqueness constraints are not met.
RAISERROR (N'Uniqueness contain violation, the covariant set is not unique with table "CovariateGroups".', -- Message text.
15, -- Severity,
1 -- State
); -- Second argument.
END
END
END
Method 2 - View with trigger
The second method involves using a views and creating an instead of insert trigger on the view.
First we create the view as follow:
CREATE VIEW CovariateGroupsView
AS
SELECT [ID]
,[Rank]
,[Covariate]
FROM CovariateGroups
Then we create the trigger:
ALTER TRIGGER CovariateGroupsViewInsteadOfInsert on CovariateGroupsView
INSTEAD OF INSERT
AS
BEGIN
DECLARE #groupID INT;
DECLARE #groupSize INT;
DECLARE #groupMatchCount INT;
DECLARE #minRank INT;
DECLARE #maxRankDelta INT;
DECLARE #minRankDelta INT;
-- Get the size of the new group which user will attempt to add.
SELECT #groupSize = COUNT([Rank])
FROM inserted
-- Validate that the new group rank starts at 1 and increments by 1 step value only.
SELECT #minRank = ISNULL(MIN([Rank]), 0)
,#maxRankDelta = ISNULL(MAX(Delta), 0)
,#minRankDelta = ISNULL(MIN(Delta), 0)
FROM (
SELECT [Rank]
,[Rank] - (LAG([Rank], 1, 0) OVER (ORDER BY [Rank])) AS Delta
FROM inserted
) RankValidation
IF ( (#minRank > 1) OR (#maxRankDelta > 1) OR (#minRankDelta < 1) )
BEGIN
RAISERROR (N'Attempting to add covariant group with invalid rank order.', -- Message text.
15, -- Severity,
1 -- State
); -- Second argument.
END
ELSE
BEGIN
-- Generate a new group ID
INSERT INTO [dbo].[CovariateGroupIDs]
(
[CreatedDateTime]
)
SELECT GETDATE() AS [CreatedDateTime]
SET #groupID = SCOPE_IDENTITY();
WITH CTE_GroupsCompareSize
AS
(
-- Compare the size of the new group with all of the existing groups. If the size is different we can
-- safely assume that the group is either a sub set or super set of the compared group. These groups
-- can be excluded from further consideration.
SELECT [CovariateGroups].[ID]
,[CovariateGroups].[Rank]
,[CovariateGroups].[Covariate]
,COUNT([CovariateGroups].[Rank]) OVER (PARTITION BY [CovariateGroups].[ID]) GroupSize
,#groupSize AS NewGroupSize
FROM [CovariateGroups]
)
,CTE_GroupsCompareRank
AS
(
-- For groups of the same size left outer join the new group on the original groups on both rank and covariant entry.
-- If the MIN() OVER window function return a value of 0 then there is at least on entry in the compared groups that does
-- not match and is therefore deemed different.
SELECT [OrginalGroup].[ID]
,[OrginalGroup].[Rank]
,[OrginalGroup].[Covariate]
,MIN(
CASE
WHEN [NewGroup].[Covariate] IS NULL THEN 0
ELSE 1
END
) OVER (PARTITION BY [OrginalGroup].[ID]) AS EntireGroupRankMatch
FROM CTE_GroupsCompareSize [OrginalGroup]
LEFT OUTER JOIN inserted [NewGroup] ON ([OrginalGroup].[Rank] = [NewGroup].[Rank] AND [OrginalGroup].[Covariate] = [NewGroup].[Covariate])
WHERE GroupSize = NewGroupSize
)
SELECT #groupMatchCount = COUNT(EntireGroupRankMatch)
FROM CTE_GroupsCompareRank
WHERE EntireGroupRankMatch = 1
IF ISNULL(#groupMatchCount, 0) = 0
BEGIN
INSERT INTO [CovariateGroups]
(
[ID]
,[Rank]
,[Covariate]
)
SELECT #groupID AS [ID]
,[Rank]
,[Covariate]
FROM inserted
END
ELSE
BEGIN
RAISERROR (N'Uniqueness contain violation, the covariant set is not unique with table "CovariateGroups".', -- Message text.
15, -- Severity,
1 -- State
); -- Second argument.
END
END
END;
The following example show how the stored procedure should be executed:
DECLARE #covariateGroupEntry AS dbo.CovariateGroupEntry
-- INSERT GROUP 1 -------------------
INSERT INTO #covariateGroupEntry
(
[Rank]
,[Covariate]
)
SELECT 1 ,'Age' UNION ALL
SELECT 2 ,'Gender' UNION ALL
SELECT 3 ,'YearOfBirth'
EXEC CovariateGroup_Add #covariateGroupEntry
Following example shows how to insert a group using the view:
DECLARE #covariateGroupEntry AS dbo.CovariateGroupEntry
-- INSERT GROUP 1 -------------------
INSERT INTO #covariateGroupEntry
(
[Rank]
,[Covariate]
)
SELECT 1 ,'Age' UNION ALL
SELECT 2 ,'Gender' UNION ALL
SELECT 3 ,'YearOfBirth'
INSERT INTO [dbo].[CovariateGroupsView]
(
[Rank]
,[Covariate]
)
SELECT [Rank]
,[Covariate]
FROM #covariateGroupEntry
DELETE #covariateGroupEntry -- Delete our memory table if we intend to use it again.
In general I would avoid using the view method since it will be susceptible to more edge cases than the stored procedure and can have some unexpected behaviors. Example the following call:
INSERT INTO [dbo].[CovariateGroupsView]
(
[Rank]
,[Covariate]
)
SELECT 1 ,'Age' UNION ALL
SELECT 2 ,'Gender' UNION ALL
SELECT 3 ,'YearOfBirth'
Will not work as expected since the trigger on the view will treat every row as a separate data set / group. As a result the validation checks will fail.
It's obvious that there is no way to produce an enforceable unique constraint that repeats across multiple rows, because if it repeats then it is not unique.
There are, however, many clever ways to create a simple check that ensures that a grouping of your Covariate values will not be inserted in multiple times.
In terms of simplicity the below SQL will produce two columns: An ID, and the ordered occurance of the covariate values:
CREATE TABLE #tmp_Covariate (ID INT, RANK INT, Covariate VARCHAR(24))
INSERT INTO #tmp_Covariate (ID, RANK, Covariate)
VALUES (1,1,'Age')
,(1,2,'Gender')
,(1,3,'YearOfBirth')
,(2,1,'Gender')
SELECT DISTINCT ID
,STUFF((SELECT N', ' + CAST(C2.[Covariate] AS VARCHAR(256))
FROM #tmp_Covariate C2
WHERE C1.ID = C2.ID
ORDER
BY C2.ID,C2.RANK
FOR XML PATH ('')),1,2,'') AS GroupCovariate
FROM #tmp_Covariate C1
The results of the SELECT are as follows:
ID GroupCovariate
1 Age, Gender, YearOfBirth
2 Gender
If a third group is added to the table, where the covariate values are:
ID Rank Covariate
2 Age
1 Gender
3 YearOfBirth
Then the ordered occurance of the Covariates do not match the GroupCovariate column returned above.
If I were solving this, I'd create a function which accepts a table valued parameter. Feed your inputs that need to be checked against the table into the table exactly as they would appear if committed successfully.
DECLARE #TVP TABLE (Rank INT, Covariate VARCHAR(24))
INSERT INTO #TVP(Rank, Covariate) VALUES (1,'Age'),(2,'Gender'),(3,'YearOfBirth')
SELECT COUNT(CheckTable.GroupCovariate) AS Exist
FROM (SELECT STUFF((SELECT N', ' + CAST(C2.[Covariate] AS VARCHAR(256))
FROM #TVP C2
ORDER
BY C2.RANK
FOR XML PATH ('')),1,2,'') AS GroupCovariate
) AS InputTable
JOIN (SELECT DISTINCT ID
,STUFF((SELECT N', ' + CAST(C2.[Covariate] AS VARCHAR(256))
FROM #tmp_Covariate C2
WHERE C1.ID = C2.ID
ORDER
BY C2.ID,C2.RANK
FOR XML PATH ('')),1,2,'') AS GroupCovariate
FROM #tmp_Covariate C1) AS CheckTable
ON CheckTable.GroupCovariate = InputTable.GroupCovariate
Because the supplied group of covariates already exists in the table, the output will be 1 (can be returned as bool for true, or 0 for false if no group does not exist).
Exist
1
If I supply "FavoriteColor" as part of my covariants:
DECLARE #TVP TABLE (Rank INT, Covariate VARCHAR(24))
INSERT INTO #TVP(Rank, Covariate) VALUES (1,'FavoriteColor'),(2,'Gender'),(3,'YearOfBirth')
SELECT COUNT(CheckTable.GroupCovariate) AS Exist
FROM (SELECT STUFF((SELECT N', ' + CAST(C2.[Covariate] AS VARCHAR(256))
FROM #TVP C2
ORDER
BY C2.RANK
FOR XML PATH ('')),1,2,'') AS GroupCovariate
) AS InputTable
JOIN (SELECT DISTINCT ID
,STUFF((SELECT N', ' + CAST(C2.[Covariate] AS VARCHAR(256))
FROM #tmp_Covariate C2
WHERE C1.ID = C2.ID
ORDER
BY C2.ID,C2.RANK
FOR XML PATH ('')),1,2,'') AS GroupCovariate
FROM #tmp_Covariate C1) AS CheckTable
ON CheckTable.GroupCovariate = InputTable.GroupCovariate
my result is 0:
Exist
0

Prevent a value from being entered if it's a prefix of another value

How could I prevent a value from being entered that is a prefix of another value in the same column? For example, if MyTable.NumberPrefix already contains abc then ab can't be added.
My first attempt (below) was to use an indexed view. But a unique index cannot be created on a view that uses a derived table (and I can't figure out how to write the view without it).
create view MyTable
with schemabinding
as
select
left(a.NumberPrefix, b.Length) as CommonPrefix
from
dbo.MyTable a
cross join
(
select distinct
len(NumberPrefix) as Length
from
dbo.MyTable
) b
create unique clustered index MyIndex on MyTable (CommonPrefix) --ERROR
Some test data:
insert MyTable (NumberPrefix) values ('abc') -- OK
insert MyTable (NumberPrefix) values ('ab') -- Error
insert MyTable (NumberPrefix) values ('a') -- Error
insert MyTable (NumberPrefix) values ('abd') -- OK
insert MyTable (NumberPrefix) values ('abcd') -- Error
Use check constraint with user defined function:
create function fnPrefix(#prefix varchar(100))
returns bit
as
begin
if (select count(*) from MyTable
where MyColumn like #prefix + '%' or #prefix like MyColumn + '%') > 1
return 0
return 1
end
Then add constraint:
alter table MyTable
add constraint chkPrefix check(dbo.fnPrefix(MyColumn) = 1)

insert data form one table to other in SQL

I need to do the following to calculate customer reviews and show them on the page:
STEP 1.
Select data from T1 , count records and calculate the rating
SELECT COUNT(Rating) As ReviewCount,
ObjectID as ObID,
SUM(Rating)/COUNT(Rating) As ReviewScore
FROM [dbo].[Comment]
WHERE ObjectTypeID ='2' AND StatusID ='2'
GROUP BY ObjectID
This code works fine.
Next I want t do is insert calculated ReviewScore value into T2
INSERT INTO [dbo].[AttributeValue] (AttributeID, Value, SortOrder)
VALUES (5,ReviewScore, 29)
STEP 2
Next I want to do is get Identity (Last inserted ID) per each item for example:
DECLARE #ID INT= ##IDENTITY
STEP3
I want to get the ##Identity per inserted row in STEP 2 and Insert new data:
------- AtribbuteID is always 5
---- ObID is taken from the STEP 1
INSERT INTO [dbo].[AttributeObjectValue]
([AttributeID],[ObjectID],[Value],[AttributeValueID])
VALUES (5,ObID,ReviewScore,#ID)
I am trying to use the CTE table but cant get it to work:
BEGIN TRAN
GO
;WITH CTE (ReviewCount, ObjectID, ReviewScore)
AS
(
SELECT COUNT(Rating) As ReviewCount,
ObjectID as ObID,
SUM(Rating)/COUNT(Rating) As ReviewScore
FROM [dbo].[Comment]
WHERE ObjectTypeID ='2' AND StatusID ='2'
GROUP BY ObjectID
)
INSERT INTO [dbo].[AttributeValue] (AttributeID, Value, SortOrder)
VALUES (5,ReviewScore, 29)
SELECT ReviewCount FROM CTE
DECLARE #ID INT= ##IDENTITY
INSERT INTO [dbo].[AttributeObjectValue]
([AttributeID],[ObjectID],[Value],[AttributeValueID])
VALUES (5,ObID,ReviewCount,#ID)
GO
ROLLBACK TRAN
I will appreciate any help

Resources