创建一个组,其中一行中两列的值与另一行的两列中的值匹配

时间:2016-02-09 17:52:06

标签: sql sql-server sql-server-2012

我正在尝试创建一个组ID,其中表中两列的值与另一行的两列中的任一值匹配。例如:

表:

+---------+-------------------+
| ogr_fid | comapared_ogr_fid |
+---------+-------------------+
|       8 |                10 |
|       6 |                 8 |
|      10 |                 4 |
|       5 |                 3 |
|       5 |                 2 |
|      12 |                15 |
+---------+-------------------+

理想输出:

+---------+-------------------+-------+
| ogr_fid | comapared_ogr_fid | SP_ID |
+---------+-------------------+-------+
|       8 |                10 |     1 |
|       6 |                 8 |     1 |
|      10 |                 4 |     1 |
|       5 |                 3 |     2 |
|       5 |                 2 |     2 |
|      12 |                15 |     3 |
+---------+-------------------+-------+

有关如何实现这一目标的任何想法?我一直在尝试使用光标。以下是我的代码。它似乎工作,但返回重复。注释掉的代码不起作用,它返回错误:

  

警告:聚合或其他SET操作消除了空值。

IF OBJECT_ID('SpatialAggregation.dbo.zzzInitialStack') IS NOT NULL -- Drop temp table if exists
    DROP TABLE dbo.zzzInitialStack;
GO

CREATE TABLE dbo.zzzInitialStack
    (
        SP_ID int,
        ogr_fid int,
        compared_ogr_fid int
    )
GO

IF OBJECT_ID('tempdb.dbo.#Stack') IS NOT NULL
    DROP TABLE #Stack
;

SELECT ogr_fid
      ,compared_ogr_fid
    INTO #Stack
    FROM dbo.zzzCentroidCalc
    WHERE CentroidDistance < 20 AND AvgScore >= 0.70 AND ogr_fid IS NOT NULL AND compared_ogr_fid IS NOT NULL

-- Declare Initial Variables
DECLARE @ogr_fid int, @compared_ogr_fid int, @sp_id int, @sp int, @in_grp int 

-- set SP ID group to 1
SET @sp_id = 1


-- create Cursor
DECLARE sp_cursor CURSOR FOR
SELECT ogr_fid
       ,compared_ogr_fid
    FROM #Stack
    ORDER BY ogr_fid, compared_ogr_fid 

OPEN sp_cursor

--Select next row from cursor
FETCH NEXT FROM sp_cursor
INTO @ogr_fid, @compared_ogr_fid

WHILE @@FETCH_STATUS = 0 
--Start Stacked Parcel grouping
BEGIN
    --IF (SELECT COUNT(*) FROM dbo.zzzInitialStack) IS NULL
    --  BEGIN
    --  INSERT INTO dbo.zzzInitialStack (SP_ID, ogr_fid,compared_ogr_fid)
    --      VALUES(@sp_id,@ogr_fid,@compare_ogr_fid)
    --  SET @sp_id = @sp_id + 1
    --  END
    --ELSE IF (SELECT count(*) FROM dbo.zzzInitialStack WHERE @ogr_fid = ogr_fid) IS NOT NULL 
    --  BEGIN
    --  SET @sp = (SELECT MAX(SP_ID) FROM dbo.zzzInitialStack WHERE @ogr_fid = ogr_fid)
    --  INSERT INTO dbo.zzzInitialStack(SP_ID, ogr_fid,compared_ogr_fid)
    --      VALUES(@sp,@ogr_fid,@compare_ogr_fid)
 --     END
    --ELSE IF (SELECT COUNT(*) FROM dbo.zzzInitialStack WHERE @compared_ogr_fid = ogr_fid) IS NOT NULL
    --  BEGIN
    --    SET @sp = (SELECT MAX(SP_ID) FROM dbo.zzzInitialStack WHERE @compared_ogr_fid = ogr_fid)
    --  INSERT INTO dbo.zzzInitialStack(SP_ID, ogr_fid,compared_ogr_fid)
    --      VALUES(@sp,@ogr_fid,@compared_ogr_fid)
    --  END
    --ELSE
    --  BEGIN
    --    INSERT INTO dbo.zzzInitialStack(SP_ID, ogr_fid, compared_ogr_fid)
    --      VALUES(@sp_id,@ogr_fid,@compared_ogr_fid)
    --  SET @sp_id = @sp_id + 1
    --  END

    INSERT INTO dbo.zzzInitialStack (SP_ID,ogr_fid,compared_ogr_fid)
    SELECT CASE WHEN @ogr_fid IN (SELECT ogr_fid FROM dbo.zzzInitialStack) OR @ogr_fid IN (SELECT ogr_fid FROM dbo.zzzInitialStack)
                    OR @compared_ogr_fid IN (SELECT ogr_fid FROM dbo.zzzInitialStack) OR @compared_ogr_fid IN (SELECT ogr_fid FROM dbo.zzzInitialStack) 
                THEN (SELECT TOP 1 SP_ID FROM dbo.zzzInitialStack
                            WHERE @ogr_fid = ogr_fid OR @ogr_fid = compared_ogr_fid
                                  OR @compared_ogr_fid = ogr_fid OR @compared_ogr_fid = compared_ogr_fid) 
                ELSE (SELECT CASE WHEN MAX(SP_ID) IS NULL 
                                  THEN 1
                                  ELSE CAST(MAX(SP_ID) + 1 AS INT)
                                  END
                            FROM dbo.zzzInitialStack) 
            END AS SP_ID
          ,@ogr_fid AS ogr_fid
          ,@compared_ogr_fid AS compared_ogr_fid

FETCH NEXT FROM sp_cursor INTO @ogr_fid, @compared_ogr_fid
END;

CLOSE sp_cursor;
DEALLOCATE sp_cursor;
GO

IF OBJECT_ID('dbo.StackedParcels') IS NOT NULL
    DROP TABLE dbo.StackedParcels;

WITH cte1 AS (
    SELECT DISTINCT SP_ID, ogr_fid
        FROM dbo.zzzInitialStack
  UNION ALL
    SELECT DISTINCT SP_ID, compared_ogr_fid AS ogr_fid
        FROM dbo.zzzInitialStack
             )
,cte2 AS (
    SELECT SP_ID, count(ogr_fid) AS NumParcels
      FROM cte1 
      GROUP BY SP_ID
         )
SELECT a.SP_ID
      ,b.ogr_fid
      ,a.NumParcels
    INTO dbo.StackedParcels
    FROM cte2 AS a JOIN cte1 AS b ON a.SP_ID = b.SP_ID

1 个答案:

答案 0 :(得分:1)

奇怪的是,我最近才解决了very similar problem。所以我基本上使用了我在那里给出的解决方案,调整到你的数据字段。

请注意,此脚本不处理NULL值。如果源表中包含NULL值,请在生成集ID之前将其替换为INT(-2147483648)的最小值。使用NULLIF在最终NULL语句中将其替换为SELECT

SET NOCOUNT ON;
CREATE TABLE #id(ID INT IDENTITY(1,1) PRIMARY KEY,ogr_fid INT,comapared_ogr_fid INT);
INSERT INTO #id(ogr_fid,comapared_ogr_fid) VALUES
    (8,10),
    (6,8),
    (10,4),
    (5,3),
    (5,2),
    (12,15);

CREATE TABLE #sets(set_id INT,fid INT PRIMARY KEY);

DECLARE c_id CURSOR FAST_FORWARD FOR
    SELECT ogr_fid,comapared_ogr_fid FROM #id;

OPEN c_id;
DECLARE @ogr_fid INT;
DECLARE @comapared_ogr_fid INT;
DECLARE @last_created_set_id INT=0;
WHILE 1=1
BEGIN
    FETCH NEXT FROM c_id INTO @ogr_fid,@comapared_ogr_fid;
    IF @@FETCH_STATUS<>0 BREAK;

    DECLARE @set_id_1 INT=(SELECT set_id FROM #sets WHERE fid=@ogr_fid);
    DECLARE @set_id_2 INT=(SELECT set_id FROM #sets WHERE fid=@comapared_ogr_fid);

    IF @set_id_1 IS NOT NULL AND @set_id_2 IS NOT NULL 
        CONTINUE;
    ELSE IF @set_id_1 IS NOT NULL
        INSERT INTO #sets(set_id,fid)VALUES(@set_id_1,@comapared_ogr_fid);
    ELSE IF @set_id_2 IS NOT NULL
        INSERT INTO #sets(set_id,fid)VALUES(@set_id_2,@ogr_fid);
    ELSE
    BEGIN
        DECLARE @id_min INT=CASE WHEN @ogr_fid<@comapared_ogr_fid THEN @ogr_fid ELSE @comapared_ogr_fid END;
        DECLARE @id_check INT;
        WHILE 1=1
        BEGIN
            SET @id_check=(
                SELECT MIN(identifier)
                FROM (
                    SELECT MIN(ogr_fid) FROM #id WHERE comapared_ogr_fid=@id_min AND ogr_fid<@id_min HAVING MIN(ogr_fid) IS NOT NULL
                    UNION ALL
                    SELECT MIN(comapared_ogr_fid) FROM #id WHERE ogr_fid=@id_min AND comapared_ogr_fid<@id_min HAVING MIN(comapared_ogr_fid) IS NOT NULL
                ) AS lu(identifier)
            );
            IF @id_check IS NULL
                BREAK;
            SET @id_min=@id_check;
        END

        DECLARE @set_id_min INT=(SELECT set_id FROM #sets WHERE fid=@id_min);
        IF @set_id_min IS NULL
        BEGIN
            SET @last_created_set_id=@last_created_set_id+1;
            SET @set_id_min=@last_created_set_id;
            INSERT INTO #sets(set_id,fid)VALUES(@set_id_min,@id_min);
        END
        IF @ogr_fid<>@id_min
            INSERT INTO #sets(set_id,fid)VALUES(@set_id_min,@ogr_fid);
        IF @comapared_ogr_fid NOT IN (@ogr_fid,@id_min)
            INSERT INTO #sets(set_id,fid)VALUES(@set_id_min,@comapared_ogr_fid);
    END
END
CLOSE c_id;
DEALLOCATE c_id;

SELECT 
    id.ogr_fid,
    id.comapared_ogr_fid,
    s.set_id AS Gr_ID 
FROM 
    #id AS id
    INNER JOIN #sets AS s ON
        s.fid=id.ogr_fid
ORDER BY 
    s.set_id,
    id.ID;

DROP TABLE #sets;
DROP TABLE #id;

结果:

+---------+-------------------+-------+
| ogr_fid | comapared_ogr_fid | SP_ID |
+---------+-------------------+-------+
|       8 |                10 |     1 |
|       6 |                 8 |     1 |
|      10 |                 4 |     1 |
|       5 |                 3 |     2 |
|       5 |                 2 |     2 |
|      12 |                15 |     3 |
+---------+-------------------+-------+