T-SQL hierarchy - get breadcrumbs using query - sql-server

I have virtual folder structure saved in database and I want to get the breadcrumbs from the current folder to the root. The data can be unsorted (but better will be sorted) and I want the parent folders of the current folder only.
The table definition is:
DECLARE Folders TABLE (
FOL_PK INT IDENTITY(1,1) NOT NULL,
FOL_Name VARCHAR(200) NOT NULL,
FOL_FOL_FK INT NULL -- Foreign key to parent
)
And this is my solution:
DECLARE #FOL_PK INT = 5 -- Current folder PK
DECLARE #breadcrumbs TABLE (
FOL_PK INT NOT NULL,
FOL_Name VARCHAR(200) NOT NULL,
FOL_FOL_FK INT NULL
)
DECLARE #isRoot BIT = 0
,#currentFolderPK INT
,#parentFK INT
-- Get current and parent folder PK
SELECT
#currentFolderPK = FOL_PK
FROM
Folder
WHERE
FOL_PK = #FOL_PK
-- Breadcrumb
WHILE (#isRoot = 0)
BEGIN
-- Save to breadcrumb
INSERT INTO #breadcrumbs
SELECT
FOL_PK,
FOL_Name,
FOL_FOL_FK
FROM
Folder
WHERE
FOL_PK = #currentFolderPK
-- Set parent as current
SET #currentFolderPK =
(
SELECT
FOL_FOL_FK
FROM
Folder
WHERE
FOL_PK = #currentFolderPK
)
-- Set flag for loop
SET #isRoot = CASE
WHEN ISNULL(#currentFolderPK, 0) = 0 THEN 1
ELSE 0
END
END
-- Return breadcrumbs
SELECT
FOL_PK AS PK,
FOL_Name AS Name,
FOL_FOL_FK AS ParentFK
FROM
#breadcrumbs
The problem is I am not very comfortable with the loop. Is there any other sophisticated solution how to do this?

Try this using a recursive Common Table Expression (CTE):
SQL Fiddle
MS SQL Server 2008 Schema Setup:
CREATE TABLE [Folders](
[FOL_PK] [int] IDENTITY(1,1) NOT NULL,
[FOL_Name] [varchar](200) NOT NULL,
[FOL_FOL_FK] [int] NULL,
CONSTRAINT [PK__Folders__FOL_PK] PRIMARY KEY CLUSTERED
(
[FOL_PK] ASC
))
ALTER TABLE [dbo].[Folders]
WITH CHECK ADD CONSTRAINT [FK_Folders_Folders] FOREIGN KEY([FOL_FOL_FK])
REFERENCES [dbo].[Folders] ([FOL_PK])
ALTER TABLE [dbo].[Folders] CHECK CONSTRAINT [FK_Folders_Folders]
INSERT INTO Folders(FOL_Name, FOL_FOL_FK)
VALUES ('Level 1', NULL),
('Level 1.1', 1),
('Level 1.2', 1),
('Level 1.3', 1),
('Level 1.2.1', 3),
('Level 1.2.2', 3),
('Level 1.2.3', 3),
('Level 1.2.2.1', 6),
('Level 1.2.2.2', 6),
('Level 1.2.2.3', 6),
('Level 1.3.1', 4),
('Level 1.3.2', 4)
Query 1:
DECLARE #FolderId Int = 9
;WITH CTE
AS
(
SELECT FOL_PK AS PK, FOL_NAME As Name, FOL_FOL_FK AS ParentFK
FROM Folders
WHERE FOL_PK = #FolderId
UNION ALL
SELECT F.FOL_PK AS PK, F.FOL_NAME AS Name, F.FOL_FOL_FK AS ParentFK
FROM Folders F
INNER JOIN CTE C
ON C.ParentFK = F.FOL_PK
)
SELECT *
FROM CTE
Results:
| PK | Name | ParentFK |
|----|---------------|----------|
| 9 | Level 1.2.2.2 | 6 |
| 6 | Level 1.2.2 | 3 |
| 3 | Level 1.2 | 1 |
| 1 | Level 1 | (null) |

Related

Query items that don't have a related record in link table but return results with Id from link table

I have an Item table:
Id | Title | Active
====================
1 | Item 1 | 1
2 | Item 2 | 1
A Location table:
Id | Name
=========
1 | A1
2 | B1
and a link table, where EventId specifies a cycle count event:
Id | EventId | ItemId | LocationId
=============|====================
1 | 1 | 1 | 2
2 | 1 | 2 | 1
3 | 2 | 1 | 1
4 | 2 | 2 | 2
5 | 3 | 1 | 1
I need to determine what items haven't been cycle-counted for a specified EventId (which in this example would be ItemId 2 for EventId 3). We're using a code generation tool that only supports tables and views with a simple filter, so I can't use a sproc or table-valued function. Ideally we'd like to be to do this:
SELECT [EventId], [ItemId] FROM [SomeView] WHERE [EventId] = 3
and get a result like
EventId | ItemId
================
3 | 2
I've tried to wrap my head around this -- unsuccessfully -- because I know it's difficult to query a negative. Is this even possible?
Is something like the following what you're after?
select l.eventId, x.Id ItemId
from Link l
cross apply (
select *
from Items i
where i.Id != l.ItemId
)x
where l.EventId = 3;
--data to work with
DECLARE #items TABLE (ID int, Title nvarchar(100), Active int)
INSERT INTO #items VALUES (1, 'Item 1', 1)
INSERT INTO #items VALUES (2, 'Item 2', 1)
DECLARE #location TABLE (ID int, Name nvarchar(100))
INSERT INTO #location VALUES (1, 'A1')
INSERT INTO #location VALUES (2, 'B1')
DECLARE #linkTable TABLE (ID int, EventId int, ItemId int, LocationId int)
INSERT INTO #linkTable VALUES (1, 1, 1, 2)
INSERT INTO #linkTable VALUES (2, 1, 2, 1)
INSERT INTO #linkTable VALUES (3, 2, 1, 1)
INSERT INTO #linkTable VALUES (4, 2, 2, 2)
INSERT INTO #linkTable VALUES (5, 3, 1, 1)
INSERT INTO #linkTable VALUES (6, 4, 2, 1)
--query you want
SELECT 3 as EventID, ID as ItemID
FROM #items i
WHERE ID not in (SELECT ItemId
FROM #linkTable
WHERE EventId = 3)
Get all the ItemIDs from the LinkTable and then get all the items from the Items table that dont have the sync event. You can replace the 3 in WHERE and SELECT clauses with whatever event you are looking for. And if you want all such pairs of event + item then this should do it:
SELECT subData.EventId, subData.ItemID
FROM (SELECT i.ID as ItemID, cj.EventId
FROM #items i CROSS JOIN (SELECT DISTINCT EventId
FROM #linkTable) cj) subData
left join #linkTable lt ON lt.EventId = subData.EventId and lt.ItemId = subData.ItemID
WHERE lt.ID is null
This could be heavy on performance because CROSS JOIN and DISTINCT and subjoins but it gets the job done. At 1st you create a data of all possible items and events pairs, then left join linked table to it and if the linked table's ID is null that means that there is no event + item pair which means that the item is not synced for that event.

PostgreSQL ARRAY_AGG return separate arrays

The use case is this: each user can create their own games, and keep track in which country they played a game.
I would like to create one query where I can get a list of all games for that user and in which country that game was played. I am only interested in the country id.
I have 4 tables: users, games, countries and a games_countries_xref table.
CREATE SEQUENCE countries_id_seq INCREMENT 1 MINVALUE 1 MAXVALUE 2147483647 START 1 CACHE 1;
CREATE TABLE "public"."countries" (
"id" integer DEFAULT nextval('countries_id_seq') NOT NULL,
"name" character varying(200) NOT NULL,
CONSTRAINT "countries_pkey" PRIMARY KEY ("id")
) WITH (oids = false);
INSERT INTO "countries" ("id", "name") VALUES
(1, 'USA'),
(2, 'Japan'),
(3, 'Australia');
CREATE SEQUENCE games_id_seq INCREMENT 1 MINVALUE 1 MAXVALUE 2147483647 START 3 CACHE 1;
CREATE TABLE "public"."games" (
"id" integer DEFAULT nextval('games_id_seq') NOT NULL,
"user_id" integer NOT NULL,
"name" character varying(200) NOT NULL,
CONSTRAINT "games_pkey" PRIMARY KEY ("id")
) WITH (oids = false);
INSERT INTO "games" ("id", "user_id", "name") VALUES
(1, 1, 'Monopoly'),
(2, 1, 'Zelda'),
(3, 2, 'Hide & Seek');
CREATE TABLE "public"."games_countries_xref" (
"game_id" integer NOT NULL,
"country_id" integer NOT NULL
) WITH (oids = false);
INSERT INTO "games_countries_xref" ("game_id", "country_id") VALUES
(1, 1),
(1, 2),
(1, 3),
(2, 2),
(3, 1);
CREATE SEQUENCE users_id_seq INCREMENT 1 MINVALUE 1 MAXVALUE 2147483647 START 2 CACHE 1;
CREATE TABLE "public"."users" (
"id" integer DEFAULT nextval('users_id_seq') NOT NULL,
"name" character varying(200) NOT NULL,
CONSTRAINT "users_pkey" PRIMARY KEY ("id")
) WITH (oids = false);
INSERT INTO "users" ("id", "name") VALUES
(1, 'Jack'),
(2, 'Jason');
when querying the data, I tried using ARRAY_AGG:
WITH country_ids AS (
SELECT g.user_id, ARRAY_AGG(gcx.country_id) AS country_ids
FROM games AS g
LEFT JOIN games_countries_xref AS gcx ON g.id = gcx.game_id
GROUP BY g.user_id
)
SELECT g.name, country_ids
FROM games AS g
NATURAL LEFT JOIN country_ids
WHERE g.user_id = 1
but that gives me this output:
name | country_ids
------------------
Monopoly | {1,2,3,2}
Zelda | {1,2,3,2}
while I am looking for this:
name | country_ids
------------------
Monopoly | {1,2,3}
Zelda | {2}
I know I am likely doing something wrong in the subquery, but I can't figure out what.
Any ideas?
You are on the right track with ARRAY_AGG, but just a little over aggressive with the joins. You just need a simple join (1 left, 1 inner) on the 3 tables
select g.name,array_agg(gcx.country_id) as country_ids
from games g
join users u on u.id = g.user_id
left join games_countries_xref gcx on gcx.game_id = g.id
where u.id = 1
group by g.name;
+----------+-------------+
| name | country_ids |
+----------+-------------+
| Monopoly | {1,2,3} |
| Zelda | {2} |
+----------+-------------+

Updating a table column based on the content of another column in another table

I have below table (to simplify I only show a piece of the tables as an example, not all their content):
CREATE TABLE InstArtRel
(
id INT IDENTITY(1, 1) NOT NULL,
idIns INT,
ExpDateRev DATE,
codArticle NVARCHAR(4),
PRIMARY KEY (id)
);
INSERT INTO InstArtRel (idIns, ExpDateRev, codArticle)
VALUES (17400, datefromparts(2018, 10, 1), 'X509'),
(17400, datefromparts(2020, 12, 2), 'X529'),
(17400, datefromparts(2016, 9, 10), 'T579'),
(17400, datefromparts(2017, 6, 7), 'Z669'),
(10100, datefromparts(2019, 8, 17), 'TG09'),
(10100, datefromparts(2018, 3, 28), 'TG09'),
(10100, datefromparts(2018, 4, 24), 'TG09'),
(10100, datefromparts(2016, 7, 12), 'TG09');
CREATE TABLE Installations
(
idIns INT NOT NULL,
DateIns DATETIME,
PRIMARY KEY (idIns)
);
INSERT INTO Installations (idIns, DateIns)
VALUES (17400, '2020-12-01'),
(10100, '2022-05-07');
For each idIns in table Installations I need to update its DateIns column with the ExpDateRev column in InstArtRel table based on the following assumptions:
If all codArticle column values for an IdIns in InstArtRel table are the same, then DateIns column in table Installations will be updated for the corresponding idIns with the maximum value of ExpDateRev.
Otherwise, if all codArticle column values are NOT the same for an IdIns in InstArtRel table, then the DateIns column in table Installations will be updated for the corresponding idIns with the minimun value of ExpDateRev.
Better an example... taken into account that said above, the result in this case will be:
idIns | DateIns
------+-----------
17400 | 2016-9-10
10100 | 2019-8-17
The Aggregate with CASE will help you.
Query:
SELECT idIns,CASE WHEN COUNT(DISTINCT codArticle) = 1 THEN MAX(ExpDateRev)
WHEN COUNT(DISTINCT codArticle) != 1 THEN MIN(ExpDateRev) END DateIns
FROM InstArtRel
GROUP BY idIns
Output:
| idIns | DateIns |
|-------|------------|
| 10100 | 2019-08-17 |
| 17400 | 2016-09-10 |
UPDATE Query:
UPDATE I
SET I.DateIns = R.DateIns
FROM Installations I
JOIN (
SELECT idIns,CASE WHEN COUNT(DISTINCT codArticle) = 1 THEN MAX(ExpDateRev)
WHEN COUNT(DISTINCT codArticle) != 1 THEN MIN(ExpDateRev) END DateIns
FROM InstArtRel
GROUP BY idIns
)R ON R.idIns = I.idIns
SQL Fiddle link

Migrate time when column was last changed from history table?

I would like to know when UserId was changed to the current value.
Say we got a table Foo:
Foo
Id | UserId
---+-------
1 | 1
2 | 2
Now I would need to be able to execute a query like:
SELECT UserId, UserIdModifiedAt FROM Foo
Luckily I have logged all the changes in history to table FooHistory:
FooHistory
Id | FooId | UserId | FooModifiedAt
---|-------+--------+---------------
1 | 1 | NULL | 1.1.2019 02:00
2 | 1 | 2 | 1.1.2019 02:01
3 | 1 | 1 | 1.1.2019 02:02
4 | 1 | 1 | 1.1.2019 02:03
5 | 2 | 1 | 1.1.2019 02:04
6 | 2 | 2 | 1.1.2019 02:05
7 | 2 | 2 | 1.1.2019 02:06
So all the data we need is available (above the user of Foo #1 was last modified 02:02 and the user of Foo #2 02:05). We will add a new column UserIdModifiedAt to Foo
Foo v2
Id | UserId | UserIdModifiedAt
---+--------|-----------------
1 | 1 | NULL
2 | 2 | NULL
... and set its values using a trigger. Fine. But how to migrate the history? What script would fill UserIdModifiedAt for us?
See an example of the table structure:
DROP TABLE IF EXISTS [Foo]
DROP TABLE IF EXISTS [FooHistory]
CREATE TABLE [Foo]
(
[Id] INT NOT NULL CONSTRAINT [PK_Foo] PRIMARY KEY,
[UserId] INT,
[UserIdModifiedAt] DATETIME2 -- Automatically updated based on a trigger
)
CREATE TABLE [FooHistory]
(
[Id] INT IDENTITY NOT NULL CONSTRAINT [PK_FooHistory] PRIMARY KEY,
[FooId] INT,
[UserId] INT,
[FooModifiedAt] DATETIME2 NOT NULL CONSTRAINT [DF_FooHistory_FooModifiedAt] DEFAULT (sysutcdatetime())
)
GO
CREATE TRIGGER [trgFoo]
ON [dbo].[Foo]
AFTER INSERT, UPDATE
AS
BEGIN
IF EXISTS (SELECT [UserId] FROM inserted EXCEPT SELECT [UserId] FROM deleted)
BEGIN
UPDATE [Foo] SET [UserIdModifiedAt] = SYSUTCDATETIME() FROM [inserted] WHERE [Foo].[Id] = [inserted].[Id]
END
INSERT INTO [FooHistory] ([FooId], [UserId])
SELECT [Id], [UserId] FROM inserted
END
GO
/* Test data */
INSERT INTO [Foo] ([Id], [UserId]) VALUES (1, NULL)
WAITFOR DELAY '00:00:00.010'
UPDATE [Foo] SET [UserId] = NULL
WAITFOR DELAY '00:00:00.010'
UPDATE [Foo] SET [UserId] = 1
WAITFOR DELAY '00:00:00.010'
UPDATE [Foo] SET [UserId] = 1
WAITFOR DELAY '00:00:00.010'
SELECT * FROM [Foo]
SELECT * FROM [FooHistory]
Related question: Select first row in each GROUP BY group?.
If I understand your question right, it looks like you have already answered it yourself by the way you created your trigger on dbo.Foo.
It looks like the UserIdModifiedAt is modified the first time the UserId changes and not modified when it does not change, in which case your answer is simply dbo.Foo.UserIdModifiedAt.
If you did not mean to write this trigger like that, I think it is possible to retrieve that value from FooHistory but it's much more complicated.
The code below might do what I think you were asking for
;WITH FooHistoryRanked
AS (
SELECT FH.Id, FH.FooId, FH.FooModifiedAt, FH.UserId
, RankedASC = ROW_NUMBER() OVER(PARTITION BY FH.FooId ORDER BY FooModifiedAt ASC) -- 1 = first change to that Foo record
FROM [FooHistory] FH
)
,Matches AS
(
SELECT FHR1.*
, PreviousUserId = FHR2.UserId
, PreviousFooModifiedAt = FHR2.FooModifiedAt
, PreviousHistoryId = FHR2.Id
FROM FooHistoryRanked FHR1
-- join on Foo filters on current value
INNER JOIN [Foo] F ON F.Id = FHR1.FooId
AND ( FHR1.UserId = F.UserId
OR (FHR1.UserId IS NULL AND F.UserId IS NULL)
)
-- Find preceding changes to a different value
LEFT JOIN FooHistoryRanked FHR2 ON FHR2.FooId = FHR1.FooId
AND FHR2.RankedASC = FHR1.RankedASC - 1 -- previous change
AND ( FHR2.UserId <> FHR1.UserId
OR ( FHR2.UserId IS NULL AND FHR1.UserId IS NOT NULL )
OR ( FHR2.UserId IS NOT NULL AND FHR1.UserId IS NULL )
)
)
,MatchesRanked AS
(
-- select the modifications that had a different value before OR that are the only modification
SELECT *, MatchRanked = ROW_NUMBER() OVER(PARTITION BY FooId ORDER BY Id DESC)
FROM Matches
WHERE RankedASC = 1 OR PreviousFooModifiedAt IS NOT NULL
)
SELECT *
FROM MatchesRanked
WHERE MatchRanked = 1 -- just get the last qualifying record
ORDER BY FooId, FooModifiedAt DESC, UserId;
PS:
1) Performance could be a problem if these tables were big...
2) you could probably use LAG instead of the LEFT JOIN but I am just used to do things this way...

SQL Server 2016 - Inserting remaining rows into a table leading to duplicates of existing rows

I have 4 tables: People Status, People, Codes and PeopleStatusCodes with the following schemas:
People:
[ID] INT IDENTITY (1, 1) CONSTRAINT [PK_People_ID] PRIMARY KEY,
[PersonCode] VARCHAR(MAX) NOT NULL,
[FirstName] VARCHAR(MAX) NOT NULL,
[LastName] VARCHAR(MAX) NOT NULL
PeopleStatus:
[ID] INT IDENTITY (1, 1) CONSTRAINT [PK_PeopleStatus_ID] PRIMARY KEY,
[PeopleID] VARCHAR(MAX) NOT NULL FOREIGN KEY REFERENCES [People]([ID]),
[Status] INT NOT NULL
Codes:
[ID] INT IDENTITY (1, 1) CONSTRAINT [PK_Codes_ID] PRIMARY KEY,
[CodeNumber] VARCHAR(MAX) NOT NULL,
[Name] VARCHAR(MAX) NOT NULL
PeopleStatusCodes:
[ID] INT IDENTITY (1, 1) CONSTRAINT [PK_PeopleStatusCodes_ID] PRIMARY KEY,
[PeopleStatusID] INT NOT NULL FOREIGN KEY REFERENCES [PeopleStatus]([ID]),
[CodeID] INT NOT NULL FOREIGN KEY REFERENCES [Codes]([ID]),
[Result] INT NOT NULL, --success = 1, fail=0
I am attempting to insert 3 rows of data into the PeopleStatusCodes table - 1 row where the Result = 1, and the remaining rows where Result = 0.
The code below declares 2 temporary tables - one to store the Person's PeopleStatus ID (#peopleStatus) the other to store the data (#data). It then checks that the Person does not already have an entry in the PeopleStatus table - if it does not, a new entry in the PeopleStatus table is created, and that ID is inserted into #peopleStatus. If an entry already exists, the ID of that entry is inserted into #peopleStatus.
An entry is then inserted into PeopleStatusCodes table based off #data, with Result = 1. After that, entries for the remaining Codes which do not have matching data are inserted with Result = 0.
--declare temporary tables
DECLARE #peopleStatus TABLE (peopleStatusID INT)
DECLARE #data TABLE (FirstName VARCHAR (100), LastName VARCHAR (100), Codename VARCHAR (100))
--insert data into #data
INSERT INTO #data(
[FirstName]
,[LastName]
,[Codename]
)
VALUES(
'John'
,'Smith'
,'02 - Code2'
)
--check if entry exists inside PeopleStatus and insert into #peopleStatus based on that
IF NOT EXISTS (SELECT [ps].[PersonCode] FROM PeopleStatus [ps], People [p], #data [d]
WHERE [ps].[PersonCode] = [p].[PersonCode]
AND [p].[FirstName] = [d].[FirstName]
AND [p].[LastName] = [d].[LastName])
INSERT INTO PeopleStatus (
[PersonCode]
,[Status]
)
OUTPUT inserted.[ID]
INTO #peopleStatus
SELECT
[p].[PersonCode]
,1
FROM [People] [p], #data [d]
WHERE [p].[FirstName] = [d].[FirstName]
AND [p].[LastName] = [d].[LastName]
ELSE INSERT INTO #peopleStatus (peopleStatusID)
SELECT [ps].[ID]
FROM PeopleStatus [ps], People [p], #data [d]
WHERE [ps].[PersonCode] = [p].[PersonCode]
AND [p].[FirstName] = [d].[FirstName]
AND [p].[LastName] = [d].[LastName]
--insert into PeopleStatusCodes a row of data with Result = 1 based off data stored in #data
INSERT INTO [dbo].[PeopleStatusCodes] (
[PeopleStatusID]
,[CodeID]
,[Result]
)
SELECT
[temp].[peopleStatusID]
,(SELECT ID FROM Codes WHERE CodeNumber + ' - ' + Name = [d].[Codename])
,1
FROM #peopleStatus [temp], #data [d]
--for every remaining Code in the Codes table which did not have a match with the data, insert into PeopleStatusCodes a row of data with Result = 0
DECLARE #IDColumn INT
SELECT #IDColumn = MIN(c.ID)
FROM Codes [c], PeopleStatusCodes [psc], #peopleStatus [temp]
WHERE [psc].CodeID != [c].ID
AND [psc].PeopleStatusID = [temp].peopleStatusID
WHILE #IDColumn IS NOT NULL
BEGIN
INSERT INTO [dbo].[PeopleStatusCodes] (
[PeopleStatusID]
,[CodeID]
,[Result]
)
SELECT
[temp].peopleStatusID
,#IDColumn
,0
FROM #peopleStatus [temp]
SELECT #IDColumn = MIN(c.ID)
FROM Codes [c], PeopleStatusCodes [psc], #peopleStatus [temp]
WHERE [psc].CodeID != [c].ID
AND [psc].PeopleStatusID = [temp].peopleStatusID
AND c.ID > #IDColumn
END
My problem is that when I run the code, instead of 3 entries in the PeopleStatusCodes table, I get 4 entries, with 1 entry a duplicate.
What I get:
+----+----------------+--------+--------+
| ID | PeopleStatusID | CodeID | Result |
+----+----------------+--------+--------+
| 1 | 1 | 2 | 1 |
| 2 | 1 | 1 | 0 |
| 3 | 1 | 2 | 0 |
| 4 | 1 | 3 | 0 |
+----+----------------+--------+--------+
What I want:
+----+----------------+--------+--------+
| ID | PeopleStatusID | CodeID | Result |
+----+----------------+--------+--------+
| 1 | 1 | 2 | 1 |
| 2 | 1 | 1 | 0 |
| 3 | 1 | 3 | 0 |
+----+----------------+--------+--------+
Update: I managed to solve it by going about it in a more straight forward way - insert all rows first, then update rows where necessary.
In the last pasrt, you could use a row number to remove duplicates:
;WITH ROW AS (
SELECT #IDColumn = MIN(c.ID),
ROW_NUMBER () OVER (PARTITION BY PeopleStatusID, CodeID ORDER BY
PeopleStatusID) AS ROW
FROM Codes [c], PeopleStatusCodes [psc], #peopleStatus [temp]
WHERE [psc].CodeID != [c].ID
AND [psc].PeopleStatusID = [temp].peopleStatusID
AND c.ID > #IDColumn )
SELECT * FROM ROW WHERE Row = 1
I managed to solve it by going about it a different way. Instead of inserting one row with Result = 1 followed by the remaining rows, I inserted ALL rows with default Result = 0. I then Updated the row that matched the data to have Result = 1.
--Inserts a row for every Code into PeopleStatusCodes
DECLARE #IDColumn VARCHAR (10)
SELECT #IDColumn = MIN(c.ID)
FROM Codes [c]
WHILE #IDColumn IS NOT NULL
BEGIN
INSERT INTO [dbo].[PeopleStatusCodes] (
[PeopleStatusID]
,[CodeID]
,[Result]
)
SELECT
[temp].[peopleStatusID]
,#IDColumn
,0
FROM #peopleStatus [temp]
SELECT #IDColumn = MIN(c.ID)
FROM Codes [c]
WHERE c.ID > #IDColumn
END
--Checks if the data matching row has not had Result changed to 1 already, and if so, update that row.
IF NOT EXISTS (SELECT [psc].ID
FROM PeopleStatusCodes [psc], #peopleStatus [temp]
WHERE [psc].PeopleStatusID = [temp].peopleStatusID
AND [psc].CodeID = (SELECT [c].ID FROM Codes [c], #data [d] WHERE [c].CodeNumber + ' - ' + [c].Name = [d].[Codename])
AND [psc].Result = 1)
UPDATE [dbo].[PeopleStatusCodes] SET Result = 1 WHERE CodeID = (SELECT [c].ID FROM Codes [c], #data [d] WHERE [c].CodeNumber + ' - ' + [c].Name = [d].[Codename])

Resources