T-SQL Select row only if not exist already - sql

I have a table with two Ids, ResourceId and LanguageId
I need to join those two selects where second result would be added only if ResourceId not already in the list.
SELECT * FROM Resources WHERE Language = 1
SELECT * FROM Resources WHERE Language = 0
JOIN
/*where ResourceId not present already*/
So far I came up with nothing except complicated partitions. Is there better solution to this?
Not all ResourceIds have Language 0 entry
Not all ResourceIds have Language 1 entry
Some ResourceIds have both
CREATE TABLE [dbo].[Resources](
[Id] [bigint] NOT NULL,
[ResourceId] [bigint] NOT NULL,
[LanguageId] [int] NOT NULL,
[Text] [nvarchar](2000) NULL,
[Path] [varchar](2000) NULL,
CONSTRAINT [PK_Resourcces] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]

You could use a union with exists logic:
SELECT * FROM Resources WHERE Language = 1
UNION ALL
SELECT *
FROM Resources r1
WHERE
Language = 0 AND
NOT EXISTS (SELECT 1 FROM Resources r2
WHERE r2.Language = 1 AND r2.ResourceId = r1.ResourceId);

You can number the rows per resourceid by languageid using the row_number() window function and then just select the "first" one.
SELECT id,
resourceid,
languageid,
text,
path
FROM (SELECT id,
resourceid,
languageid,
text,
path,
row_number() OVER (PARTITION BY resourceid
ORDER BY languageid DESC) rn
FROM resources
WHERE languageid IN (0,
1) x
WHERE rn = 1;

Since I had started answering but Tim was more effective than me, I still put my answer as you indicated that, and I quote:
If somebody finds something faster and simpler, I would love to see it
USE TEST
CREATE DATABSE TEST
CREATE TABLE Ressources
(
RessourceId INT,
LanguageId INT
);
INSERT INTO Ressources
VALUES
(1,1),
(1,0),
(1,2),
(1,3),
(2,1),
(2,0),
(2,2),
(3,1),
(4,1),
(5,0)
WITH CTE_L1 AS (SELECT * FROM Ressources WHERE LanguageId = 1)
SELECT * FROM CTE_L1
UNION ALL
SELECT * FROM Ressources
WHERE LanguageId = 0
AND RessourceId NOT IN(SELECT RessourceId FROM CTE_L1)
Results I got:
RessourceId LanguageId
----------- -----------
1 1
2 1
3 1
4 1
5 0
(Same result if I execute the #Tim Biegeleisen query)
See which one you like best.
--> Cost of mine query 0.010132
--> Cost of Tim query 0.0100952
(Based on the execution plan)

Related

Select Data with Order By Respecting Ancestor

I have a table MyStackFiles that has 3 columns:
FileID (The primary key)
FileName
OriginalFileID (This can be either 0 if there is no original file or one of the other file IDs)
My goal is to select the whole data sorted by name. In addition, I need to always have the original files appear before their children. In other words, the desired result will start with the first alphabetical file whose OriginalFileID is 0 followed by all its children (if available) alphabetically. The following SQL script creates the sample data and illustrates exactly what I'm trying to achieve. Notice that the last select command is the desired output.
What is the query that can return the desired result?
The Script:
-------------------------- Creating the Example Schema --------------------------
IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.Tables WHERE Table_Name = 'MyStackFiles')
Drop table MyStackFiles
GO
CREATE TABLE [dbo].[MyStackFiles](
[FileID] [int] IDENTITY(1,1) NOT NULL,
[FileName] [varchar](50) NULL,
[OriginalFileID] [int] NOT NULL DEFAULT (0),
CONSTRAINT [PK_MyStackFiles] PRIMARY KEY CLUSTERED
(
[FileID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
------------------------------------------------------------------------------
GO
-------------------------- Insert Into the Sample Data --------------------------
INSERT INTO MyStackFiles(FileName) values ('S')
INSERT INTO MyStackFiles(FileName) values ('G')
INSERT INTO MyStackFiles(FileName, OriginalFileID) values ('E', 1)
INSERT INTO MyStackFiles(FileName) values ('F')
INSERT INTO MyStackFiles(FileName, OriginalFileID) values ('Q', 2)
INSERT INTO MyStackFiles(FileName, OriginalFileID) values ('N', 3)
INSERT INTO MyStackFiles(FileName) values ('A')
INSERT INTO MyStackFiles(FileName, OriginalFileID) values ('X', 1)
INSERT INTO MyStackFiles(FileName) values ('W')
------------------------------------------------------------------------------
GO
-------------------------- Simple select sorted by FileName --------------------------
SELECT * From MyStackFiles ORDER BY FileName
-------------------------- A representation of the desired result --------------------------
SELECT * FROM MyStackFiles WHERE FileID = 7 UNION ALL -- We insert "A" (respecting the alphabetical order) since its OriginalFileID is 0
SELECT * FROM MyStackFiles WHERE FileID = 4 UNION ALL -- Then we insert F.
SELECT * FROM MyStackFiles WHERE FileID = 2 UNION ALL -- Then we insert G. G has children so we insert them
SELECT * FROM MyStackFiles WHERE FileID = 5 UNION ALL -- Q is the only child of G. We insert it
SELECT * FROM MyStackFiles WHERE FileID = 1 UNION ALL -- Now we insert S. Notice that S has two children (E and X)
SELECT * FROM MyStackFiles WHERE FileID = 3 UNION ALL -- E is before X alphabetically so it gets inserted first
SELECT * FROM MyStackFiles WHERE FileID = 6 UNION ALL -- E happens to have children so we insert them right away (in a depth first fashion)
SELECT * FROM MyStackFiles WHERE FileID = 8 UNION ALL -- Now we insert the other child of S which is X
SELECT * FROM MyStackFiles WHERE FileID = 9 -- Finally we insert W the only file left
--Drop Table MyStackFiles
I'm open to any schema modification if that helps find an efficient query.
I'm using the technique called Recursive CTE to try to solve your problem:
with t (RowID, FileID, FileName, OriginalFileID)
as (
select convert(varchar(max), row_number() over (order by s.FileName)), s.*
from MyStackFiles s
where s.OriginalFileID = 0
union all
select t.RowID + '.' + convert(varchar(max), row_number() over (order by s.FileName)), s.*
from MyStackFiles s
inner join t on t.FileID = s.OriginalFileID
)
select FileID, FileName, OriginalFileID from t
order by RowID
A temporary column RowID is created on-the-fly to chain up the ancestor's RowID to the row's row_number, so that for instance the file "N" will have RowID = '4.1.1', the file "X" will have RowID = '4.2', and this is the column to sort that fits your sorting requirement.

How to get result from parent child table

Work on SQL-Server. My table structure is below
CREATE TABLE [dbo].[AgentInfo](
[AgentID] [int] NOT NULL,
[ParentID] [int] NULL,
CONSTRAINT [PK_AgentInfo] PRIMARY KEY CLUSTERED
(
[AgentID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
INSERT [dbo].[AgentInfo] ([AgentID], [ParentID]) VALUES (1, -1)
INSERT [dbo].[AgentInfo] ([AgentID], [ParentID]) VALUES (2, -1)
INSERT [dbo].[AgentInfo] ([AgentID], [ParentID]) VALUES (3, 1)
INSERT [dbo].[AgentInfo] ([AgentID], [ParentID]) VALUES (4, 2)
Required output
Use my below syntax get required output but not satisfied. Is there any better way to get the required output
--get parent child list
---step--1
SELECT *
INTO #temp1
FROM ( SELECT a.AgentID ,
a.ParentID,
a.AgentID AS BaseAgent
FROM dbo.AgentInfo a WHERE ParentID=-1
UNION ALL
SELECT a.ParentID ,
0 as AgentID,
a.AgentID AS BaseAgent
FROM dbo.AgentInfo a WHERE ParentID!=-1
UNION ALL
SELECT a.AgentID ,
a.ParentID,
a.AgentID AS BaseAgent
FROM dbo.AgentInfo a
WHERE ParentID!=-1
) AS d
SELECT * FROM #temp1
DROP TABLE #temp1
Help me to improve my syntax. If you have any questions please ask.
You could use a recursive SELECT, see the examples in the documentation for WITH, starting with example D.
The general idea within the recursive WITH is: You have a first select that is the starting point, and then a UNION ALL and a second SELECT which describes the step from on level to the next, where the previous level can either be the result of the first select or the result of the previous run of the second SELECT.
You can try this, to get a tree of the elements:
WITH CTE_AgentInfo(AgentID, ParentID, BaseAgent)
AS(
SELECT
AgentID,
ParentID,
AgentID AS BaseAgent
FROM AgentInfo
WHERE ParentID = -1
UNION ALL
SELECT
a.AgentID,
a.ParentID,
a.AgentID AS BaseAgent
FROM AgentInfo a
INNER JOIN CTE_AgentInfo c ON
c.AgentID = a.ParentID
)
SELECT * FROM CTE_AgentInfo
And here is an SQLFiddle demo to see it.
Try something like this:
WITH Merged (AgentId, ParentId) AS (
SELECT AgentId, ParentId FROM AgentInfo WHERE ParentId = -1
UNION ALL
SELECT AgentInfo.AgentId, AgentInfo.ParentId FROM AgentInfo INNER JOIN Merged ON AgentInfo.AgentId = Merged.ParentId
)
SELECT * FROM Merged
You can use a Common Table Expression to do this.
The sql statement will then look like this:
WITH [Parents]([AgentID], [ParentID], [BaseAgent])
AS
(
SELECT
[AgentID],
[ParentID],
[AgentID] AS [BaseAgent]
FROM [AgentInfo]
WHERE [ParentID] = -1
UNION ALL
SELECT
[ai].[AgentID],
[ai].[ParentID],
[p].[BaseAgent]
FROM [AgentInfo] [ai]
INNER JOIN [Parents] [p]
ON [ai].[ParentID] = [p].[AgentID]
)
SELECT *
FROM [Parents]
ORDER BY
[BaseAgent] ASC,
[AgentID] ASC
But, the results are different from your desired output, since every Agent is only listed once.
The output is:
AGENTID PARENTID BASEAGENT
1 -1 1
3 1 1
2 -1 2
4 2 2
The Fiddle is over here.
And here is a nice post on working with hierarchies: What are the options for storing hierarchical data in a relational database?

How To Get A Hierarchical CTE In SQL Server To Filter With Parent and Child Logic

I'm having a vexing problem with a hierarchical CTE and some strange logic that we need to address that I really hope someone could assist with pointing out what I'm doing wrong to address this scenario with a CTE.
Here is the hierarchical data we're dealing with in this example:
This is the problematic SQL followed by the description of the problem and SQL statements to create a test table with data:
DECLARE #UserId nvarchar(50);
SET #UserId = 'A';
DECLARE #StatusType int;
SET #StatusType = '2';
;WITH recursiveItems (Id, Depth)
AS
(
SELECT Id, 0 AS Depth
FROM dbo.CteTest
WHERE UserId = #UserId
--AND StatusType = #StatusType
-- This would also be incorrect for the issue
AND ParentId IS NULL
UNION ALL
SELECT dbo.CteTest.Id, Depth + 1
FROM dbo.CteTest
INNER JOIN recursiveItems
ON dbo.CteTest.ParentId = recursiveItems.Id
WHERE UserId = #UserId
AND StatusType = #StatusType
)
SELECT A.*, recursiveItems.Depth
FROM recursiveItems
INNER JOIN dbo.CteTest A WITH(NOLOCK) ON
recursiveItems.Id = A.Id
ORDER BY A.Id
This is not returning the desired data. The data that is currently returned is in the NOT CORRECT section of the image below. The row with the Id of 10 is the row that we want to omit.
Essentially the logic should be that any parent record (record with children) where the status type of any of its children is equal to 2 should be returned along with its children. In the example this is the rows with Ids: 1, 5, 6, 7, 9.
Currently the CTE/SQL/Code is returning ALL parent records no matter what,
The record with the Id 1 should be returned, even though it's status type is 1 because at least one of its children, their children, grandchildren, etc. have a status type that is equal to 2.
The record with the Id of 10 should not be returned because it does not have a status that is equal to 2 or any children. If the record had a status type of 2 when it has no child records it should also be returned.
This is the DDL to create a test table that helps to show the problem:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[CteTest](
[Id] [int] IDENTITY(1,1) NOT NULL,
[StatusType] [int] NOT NULL,
[UserId] [nvarchar](50) NOT NULL,
[ParentId] [int] NULL,
CONSTRAINT [PK_CteTest] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
This is the seed data for the table, that can demonstrate the issue:
INSERT INTO [dbo].[CteTest]([StatusType],[UserId],[ParentId]) VALUES (1,'A',NULL)
INSERT INTO [dbo].[CteTest]([StatusType],[UserId],[ParentId]) VALUES (1,'B',NULL)
INSERT INTO [dbo].[CteTest]([StatusType],[UserId],[ParentId]) VALUES (2,'B',NULL)
INSERT INTO [dbo].[CteTest]([StatusType],[UserId],[ParentId]) VALUES (1,'A',1)
INSERT INTO [dbo].[CteTest]([StatusType],[UserId],[ParentId]) VALUES (2,'A',1)
INSERT INTO [dbo].[CteTest]([StatusType],[UserId],[ParentId]) VALUES (2,'A',5)
INSERT INTO [dbo].[CteTest]([StatusType],[UserId],[ParentId]) VALUES (2,'A',6)
INSERT INTO [dbo].[CteTest]([StatusType],[UserId],[ParentId]) VALUES (3,'A',6)
INSERT INTO [dbo].[CteTest]([StatusType],[UserId],[ParentId]) VALUES (2,'A',NULL)
INSERT INTO [dbo].[CteTest]([StatusType],[UserId],[ParentId]) VALUES (4,'A',NULL)
INSERT INTO [dbo].[CteTest]([StatusType],[UserId],[ParentId]) VALUES (3,'A',10)
The issue is that your base case includes all null (parentless) items, and there is no way to filter them out later.
Because you are looking for only items with a particular statustype, you may want to refactor the CTE; Instead of having a base case be the root values, you can have it be all items with the given statustype, and then recursively find the parents. In the solution below, I have depth be a negative number, for distance from the item with a value of 2 in the given tree (so negative height, instead of depth.).
DECLARE #UserId nvarchar(50);
SET #UserId = 'A';
DECLARE #StatusType int;
SET #StatusType = '2';
WITH recursiveItems (Id, ParentID, Depth)
AS
(
SELECT Id, ParentID, 0 AS Depth
FROM dbo.CteTest
WHERE UserId = #UserId AND StatusType = #StatusType
UNION ALL
SELECT dbo.CteTest.Id, CteTest.ParentID, Depth - 1
FROM dbo.CteTest
INNER JOIN recursiveItems
ON dbo.CteTest.Id = recursiveItems.ParentId
WHERE UserId = #UserId
)
SELECT A.Id, A.StatusType, A.UserId, A.ParentId, min(recursiveItems.Depth)
FROM recursiveItems
INNER JOIN dbo.CteTest A WITH(NOLOCK) ON
recursiveItems.Id = A.Id
group by A.Id, A.StatusType, A.UserId, A.ParentId
ORDER BY A.Id

Remove duplicate row and update next row to current row and continue

I need a select query ..
Environment : SQL DBA -SQL SERVER 2005 or newer
Example :
In this sample table, if I select top 20 no duplicate records should come and next record should be in 20 records .
Example :
123456 should not repeat in 20 records and if 18th is duplicate, in place of 18th, 19th record should come and in 19th—20th should come, in 20th ---21st should come .
No concern of Asc or Desc for rows .
Lookup Table before
Id Name
123456 hello
123456 hello
123654 hi
123655 yes
LookUp Table after
Id Name
123456 hello
123654 hi
123655 yes
My table:
CREATE TABLE [dbo].[test](
[Id] [int] IDENTITY(1,1) NOT NULL,
[ContestId] [int] NOT NULL,
[PrizeId] [int] NOT NULL,
[ContestParticipantId] [int] NOT NULL,
[SubsidiaryAnswer] [varchar](256) NOT NULL,
[SubsidiaryDifference] [bigint] NOT NULL,
[AttemptTime] [datetime] NOT NULL,
[ParticipantName] [varchar](250) NOT NULL,
[IsSubscribed] [bit] NOT NULL,
[IsNewlyRegistered] [bit] NOT NULL,
[IsWinner] [bit] NOT NULL,
[IsWinnerConfirmed] [bit] NOT NULL,
[IsWinnerExcluded] [bit] NOT NULL) ON [PRIMARY]
My question is: from this select, we actually need the first 20, but unique ones.
SELECT TOP 20 * FROM test order by SubsidiaryDifference
When we do the above query, we have currently some double in there. In case there is a double, we need take them only 1 time and take the next one
Any one know this issue ?
Thanks in advance :)
Reading your question, it appears you don't really want to delete the rows from the table - you just want to display the TOP 20 distinct rows - you try something like this:
;WITH LastPerContestParticipantId AS
(
SELECT
ContestParticipantId,
-- add whatever other columns you want to select here
ROW_NUMBER() OVER(PARTITION BY ContestParticipantId
ORDER BY SubsidiaryDifference) AS 'RowNum'
FROM dbo.Test
)
SELECT TOP (20)
ContestParticipantId,
-- add whatever other columns you want to select here
SubsidiaryDifference
FROM
LastPerContestParticipantId
WHERE
RowNum = 1
This will show you the most recent row for each distinct ContestParticipantId, order by SubsidiaryDifference - try it!
Update #2: I've created a quick sample - it uses the data from your original post - plus an additional SubID column so that I can order rows of the same ID by something...
When I run this with my CTE query, I do get only one entry for each ID - so what exactly is "not working" for you?
DECLARE #test TABLE (ID INT, EntryName VARCHAR(50), SubID INT)
INSERT INTO #test
VALUES(123456, 'hello', 1), (123456, 'hello', 2), (123654, 'hi', 1), (123655, 'yes', 3)
;WITH LastPerId AS
(
SELECT
ID, EntryName,
ROW_NUMBER() OVER(PARTITION BY ID ORDER BY SubID DESC) AS 'RowNum'
FROM #test
)
SELECT TOP (3)
ID, EntryName
FROM
LastPerId
WHERE
RowNum = 1
Gives an output of:
ID EntryName
123456 hello
123654 hi
123655 yes
No duplicates.

How to create a stored procedure to find cliques in the table of connections between users

Loooking for a way to retrieve community from a large dataset I came across an article about the algorithm which seems to be apropriate for large datasets. Anyway the data is stored two tables: users (nodes) and connections and I would like to retrieve the communities by pure sql queries without help of custom applications (I'm using SQL Server 2008).
The algorithm to retrieve the cliques is the following:
Read the graph G
Generate set neighbors(v) for every vertex of G
for each vertex v of G
call recursive_find_cliques(v, neighbors(v))
end for
Function recursive_find_cliques(x, n)
for each vertex t ∈ n by ascending order calculate set sigma
if sigma is not empty
extend x with t
call recursive_find_cliques(x, sigma)
end if
end for
where sigma is the set of vertices that could constitute triangles with v and its neighbors.
I already created a stored procedure which returns a table of neighbors of selected node but so far I haven't delat with sql functions and advanced queries so the question is the following:
Does anyone know how to rewrite the
algorithm above in sql in order to get
the set of cliques? As the question
might be a little abstract, I may
point out that the main problem is to
create a recursive function
(recursive_find_cliques(x, n)) which
takes a table (n) as an argument).
Thank you!
EDIT:
Here is sthe stored procedure created so far:
CREATE PROCEDURE [dbo].[Peamc_Test]
AS
BEGIN
SET XACT_ABORT ON
BEGIN TRAN
SET NOCOUNT ON;
CREATE TABLE #Users
(
UserId int NOT NULL,
userLabel varchar(50) PRIMARY KEY NOT NULL,
Observed bit NOT NULL
)
CREATE TABLE #Neighbors
(
UserId int NOT NULL,
userLabel varchar(50) NOT NULL PRIMARY KEY,
Retrieved bit NOT NULL
)
CREATE TABLE #ConnectedVertices
(
UserId int NOT NULL,
userLabel varchar(50) NOT NULL PRIMARY KEY,
)
CREATE TABLE #Cliques
(
CliqueId int NOT NULL,
UserId varchar(50) NOT NULL,
)
DECLARE #UsersCount int
DECLARE #ii int
DECLARE #User varchar(50)
DECLARE #NeighborsCount int
INSERT INTO #Users(UserId, userLabel, Observed) SELECT user_id, userLabel, 0 FROM dbo.test_users WHERE user_id IS NOT NULL
SELECT #UsersCount = COUNT(*) FROM #Users
SELECT #ii = 1
WHILE #ii <= #UsersCount
BEGIN
--select user
SELECT TOP 1 #User = userLabel FROM #Users WHERE Observed = 0 ORDER BY UserId
UPDATE #Users SET Observed = 1 WHERE userLabel = #User
--Get user's neighbors
DELETE FROM #Neighbors
INSERT INTO #Neighbors(UserId, userLabel, Retrieved)
SELECT u.user_id, t2.neighbor, 0 FROM ( SELECT CALLING_NEIGHBORS.neighbor FROM ( SELECT mc.calling_party AS neighbor FROM monthly_connections_test mc WHERE mc.called_party = #User) AS CALLING_NEIGHBORS INNER JOIN (SELECT mc.called_party AS neighbor FROM monthly_connections_test mc WHERE mc.calling_party = #User) AS CALLED_NEIGHBORS ON CALLING_NEIGHBORS.neighbor = CALLED_NEIGHBORS.neighbor) AS t2 INNER JOIN test_users u ON t2.neighbor = u.userLabel
SELECT #NeighborsCount = COUNT(*) FROM #Neighbors
SELECT #ii = #ii + 1
--HERE the function recursive_find_cliques has to search for cliques and insert the found ones in #cliques
END
SELECT * FROM #Cliques
END
It does'not return anything yet as it is not finished. It though retrieves all neighbors for the currently selected nodes and the next step is to implement recursive_find_cliques function.
I realised that my first answer only works when each clique has at least one user who is not referred to by any others in that clique. In other words, closed cliques like A-B, B-C, C-A will not be found.
Here is a solution which solves this. Again we have users with IDs, now 1..20. There are several cases of neighbouring relations that need to be handled:
Compared to the simple case, it is harder to find a unique starter for each clique.
We achieve this with a little sleight of hand:
Reorder the neighbours so that for all references A-B, A is less than B, ignoring any A=B.
From these, remove any A-X references if there are any X-A, which could cause a loop. This will never remove references to A completely because X-A remains and A-X will be added in the recursion.
The resultant set are the 'starting' users and we use them to prime the CTE:
-- Get all pairs, where UserA < UserB, dropping any A=B or B=A
WITH LRNeighbours(A, B) AS (
SELECT
Neighbours.UserA, Neighbours.UserB
FROM
Neighbours
WHERE
Neighbours.UserA < Neighbours.UserB
UNION ALL
SELECT DISTINCT
Neighbours.UserB, Neighbours.UserA
FROM
Neighbours
WHERE
Neighbours.UserA > Neighbours.UserB
),
-- Isolate those that are not referred to by a higher numbered key
Starters(userid) AS (
SELECT DISTINCT
A
FROM
LRNeighbours
WHERE
A NOT IN (
SELECT
B
FROM
LRNeighbours
)
),
-- The recursive Common Table Expression
cliques(userid, clique) AS (
-- Number starters 1..N
SELECT
userid, ROW_NUMBER() OVER(ORDER BY userid) AS clique
FROM
Starters
UNION ALL
-- Recurse, adding users referred by siblings, avoiding starters themselves
SELECT
B, clique
FROM
LRNeighbours INNER JOIN
cliques ON
LRNeighbours.A = cliques.userid
AND B NOT IN (
SELECT
userid
FROM
starters
)
)
SELECT DISTINCT
clique, userid
FROM
cliques
ORDER BY
clique, userid
Results:
1 1
1 2
2 3
2 4
3 5
3 6
3 7
3 8
4 9
4 10
4 11
4 12
4 13
5 14
5 15
5 16
5 17
5 18
5 19
5 20
CREATE TABLE [dbo].[Users](
[UserID] [int] IDENTITY(1,1) NOT NULL,
[UserName] [varchar](50) NOT NULL
) ON [PRIMARY]
CREATE TABLE [dbo].[Neighbours](
[UserA] [int] NOT NULL,
[UserB] [int] NOT NULL
) ON [PRIMARY]
Users populated with 1..8 and Neighbours
UserA UserB
1 2
2 3
4 5
4 6
5 7
7 8
Then:
WITH cliques(userid, clique) AS (
SELECT
userid, ROW_NUMBER() OVER(ORDER BY userid) AS clique
FROM
Users
WHERE
users.UserID NOT IN (
SELECT
UserB
FROM
Neighbours
)
UNION ALL
SELECT
Neighbours.UserB, clique
FROM
neighbours
INNER JOIN cliques
ON Neighbours.UserA = cliques.userid
)
SELECT
clique, cliques.userid
FROM
cliques
ORDER BY
clique, userid
Result:
clique userid
1 1
1 2
1 3
2 4
2 5
2 6
2 7
2 8
See : Recursive Queries Using Common Table Expressions
I've added a two LABELS and two GOTO statements
CREATE PROCEDURE [dbo].[Peamc_Test]
AS
BEGIN
SET XACT_ABORT ON
BEGIN TRAN
SET NOCOUNT ON;
CREATE TABLE #Users
(
UserId int NOT NULL,
userLabel varchar(50) PRIMARY KEY NOT NULL,
Observed bit NOT NULL
)
CREATE TABLE #Neighbors
(
UserId int NOT NULL,
userLabel varchar(50) NOT NULL PRIMARY KEY,
Retrieved bit NOT NULL
)
CREATE TABLE #ConnectedVertices
(
UserId int NOT NULL,
userLabel varchar(50) NOT NULL PRIMARY KEY,
)
CREATE TABLE #Cliques
(
CliqueId int NOT NULL,
UserId varchar(50) NOT NULL,
)
DECLARE #UsersCount int
DECLARE #ii int
DECLARE #User varchar(50)
DECLARE #NeighborsCount int
INSERT INTO #Users(UserId, userLabel, Observed) SELECT user_id, userLabel, 0 FROM dbo.test_users WHERE user_id IS NOT NULL
SELECT #UsersCount = COUNT(*) FROM #Users
SELECT #ii = 1
WHILE #ii <= #UsersCount
BEGIN
--select user
SELECT TOP 1 #User = userLabel FROM #Users WHERE Observed = 0 ORDER BY UserId
UPDATE #Users SET Observed = 1 WHERE userLabel = #User
--Get user's neighbors
DELETE FROM #Neighbors
INSERT INTO #Neighbors(UserId, userLabel, Retrieved)
SELECT u.user_id, t2.neighbor, 0 FROM ( SELECT CALLING_NEIGHBORS.neighbor FROM ( SELECT mc.calling_party AS neighbor FROM monthly_connections_test mc WHERE mc.called_party = #User) AS CALLING_NEIGHBORS INNER JOIN (SELECT mc.called_party AS neighbor FROM monthly_connections_test mc WHERE mc.calling_party = #User) AS CALLED_NEIGHBORS ON CALLING_NEIGHBORS.neighbor = CALLED_NEIGHBORS.neighbor) AS t2 INNER JOIN test_users u ON t2.neighbor = u.userLabel
SELECT #NeighborsCount = COUNT(*) FROM #Neighbors
SELECT #ii = #ii + 1
GOTO Clique_Find
--HERE the function recursive_find_cliques has to search for cliques and insert the found ones in #cliques
--------------------
Clique_Return:
--------------------
END
SELECT * FROM #Cliques
END
--------------------
Clique_Find:
--------------------
-- Code goes here
-- Code goes here
-- Code goes here
-- Code goes here
-- Code goes here
-- Code goes here
GOTO Clique_Return