使用另一个表中的唯一值创建一个表
Posted
技术标签:
【中文标题】使用另一个表中的唯一值创建一个表【英文标题】:Create a table with unique values from another table 【发布时间】:2015-09-04 02:46:11 【问题描述】:我正在使用 MS SQL Server Management Studio。我有桌子 -
+--------+----------+
| Num_ID | Alpha_ID |
+--------+----------+
| 1 | A |
| 1 | B |
| 1 | C |
| 2 | B |
| 2 | C |
| 3 | A |
| 4 | C |
| 5 | A |
| 5 | B |
+--------+----------+
我想从该表中创建另一个包含 2 列的表,以便 column_1 给出 Num_ID 中的唯一值(即 1、2、3、4 等),column_2 给出 Alpha_ID 中的唯一值(A、B、C 和很快)。
但如果一个字母表已经出现过,它就不应该再次出现。所以输出将是这样的 -
Col_1 Col_2
================
1 - A
----------------
2 - B
----------------
3 - NULL (as A has been chosen by 1, it cannot occur next to 3)
----------------
4 - C
----------------
5 - NULL (both 5 A and 5 B cannot be chosen as A and B were picked up by 1 and 2)
----------------
希望这是有道理的。 我想澄清一下,输入表中的 ID 不是我显示的数字,但 Num_ID 和 Alpha_ID 都是复杂的字符串。为了这个问题,我已将它们简化为 1,2,3,... 和 A,B,C ....
【问题讨论】:
可能想在此处处理您的格式。这是不可读的。 我开始编辑这个,但想不出一个描述性的名称,所以我继续前进。 我试图修正标题;如有错误,请指正。 @GordonLinoff 请看一下这个问题,谢谢。 使用 Microsoft SQL Server Management Studio 11.0.2100.60 【参考方案1】:我认为没有光标就无法做到这一点。 我在您的示例数据中添加了几行,以测试它如何与其他案例一起使用。
逻辑很简单。首先获取Num_ID
的所有不同值的列表。然后遍历它们并在每次迭代中向目标表添加一行。要确定要添加的 Alpha_ID
值,我将使用 EXCEPT
运算符,它从源表中获取当前 Num_ID
的所有可用 Alpha_ID
值,并从中删除之前使用过的所有值。
可以在不使用显式变量@CurrAlphaID
的情况下编写INSERT
,但使用变量看起来更简洁。
这里是SQL Fiddle。
DECLARE @TSrc TABLE (Num_ID varchar(10), Alpha_ID varchar(10));
INSERT INTO @TSrc (Num_ID, Alpha_ID) VALUES
('1', 'A'),
('1', 'B'),
('1', 'C'),
('2', 'B'),
('2', 'C'),
('3', 'A'),
('3', 'C'),
('4', 'A'),
('4', 'C'),
('5', 'A'),
('5', 'B'),
('5', 'C'),
('6', 'D'),
('6', 'E');
DECLARE @TDst TABLE (Num_ID varchar(10), Alpha_ID varchar(10));
DECLARE @CurrNumID varchar(10);
DECLARE @CurrAlphaID varchar(10);
DECLARE @iFS int;
DECLARE @VarCursor CURSOR;
SET @VarCursor = CURSOR FAST_FORWARD
FOR
SELECT DISTINCT Num_ID
FROM @TSrc
ORDER BY Num_ID;
OPEN @VarCursor;
FETCH NEXT FROM @VarCursor INTO @CurrNumID;
SET @iFS = @@FETCH_STATUS;
WHILE @iFS = 0
BEGIN
SET @CurrAlphaID =
(
SELECT TOP(1) Diff.Alpha_ID
FROM
(
SELECT Src.Alpha_ID
FROM @TSrc AS Src
WHERE Src.Num_ID = @CurrNumID
EXCEPT
SELECT Dst.Alpha_ID
FROM @TDst AS Dst
) AS Diff
ORDER BY Diff.Alpha_ID
);
INSERT INTO @TDst (Num_ID, Alpha_ID)
VALUES (@CurrNumID, @CurrAlphaID);
FETCH NEXT FROM @VarCursor INTO @CurrNumID;
SET @iFS = @@FETCH_STATUS;
END;
CLOSE @VarCursor;
DEALLOCATE @VarCursor;
SELECT * FROM @TDst;
结果
Num_ID Alpha_ID
1 A
2 B
3 C
4 NULL
5 NULL
6 D
在源表上的(Num_ID, Alpha_ID)
上有索引会有所帮助。在目标表上对(Alpha_ID)
进行索引也会有所帮助。
【讨论】:
我认为也许我们可以使用递归 CTE,以便我们可以跟踪我们选择时已经采用了哪些 alpha 值,但是您不能在子选择中使用递归,因此很遗憾它不起作用。我想 CURSOR 是唯一的选择。 @plalx,也许不用游标也可以,但肯定需要自连接,可能不止一次。结果性能可能很差,可能比直接游标更差。而且很可能很难理解逻辑并在以后维护此代码。有时光标很有用。 我认为这是可以接受CURSOR
的地方。赞一个!【参考方案2】:
我想我不是通过递归(光标或一段时间)做了一些事情
首先,我创建了一个包含行的表。
create table #tmptest
(
Num_ID int
, Alpha_ID varchar(50)
)
insert into #tmptest (Num_ID, Alpha_ID) values
(1,'A'),
(1,'B'),
(1,'C'),
(2,'B'),
(2,'C'),
(3,'A'),
(4,'C'),
(5,'A'),
(5,'B')
// this one, with row column
SELECT
ROW_NUMBER() OVER (PARTITION BY Num_ID ORDER BY Num_ID ASC) as row
, *
INTO #tmp_withrow
FROM #tmptest
这些就是结果
最后,我做了一个内部查询(可能是左连接或更好)。
SELECT DISTINCT
Num_ID
, (
SELECT
TOP 1
Alpha_ID
FROM #tmp_withrow in1
WHERE
in1.Num_ID = t.Num_ID
AND in1.Alpha_ID NOT IN (
SELECT
Alpha_ID
FROM #tmp_withrow in2
WHERE
in2.Num_ID < in1.Num_ID
AND in2.row = 1
)
ORDER BY in1.Num_ID ASC
) AS [NonRepeatingAlpha]
from #tmptest t
这些就是结果
注意:我创建了一个标志 (row
),它允许您查询所有小于您所在 ID (in2.Num_ID < in1.Num_ID
) 的字母,然后找出已使用的字母 (in2.row = 1
),然后选择/ 避免其他 Num_ID 中已经使用过的所有字母(
WHERE in1.Num_ID = t.Num_ID
AND in1.Alpha_ID NOT IN (
SELECT
Alpha_ID
FROM #tmp_withrow in2
WHERE
in2.Num_ID < in1.Num_ID
AND in2.row = 1
)
我希望这会有所帮助。谢谢!
【讨论】:
我没有详细查看您的查询,但将(3,'C')
添加到您的测试数据中,很遗憾您会看到结果不正确。
是的,我很抱歉,但我会回来的! :)以上是关于使用另一个表中的唯一值创建一个表的主要内容,如果未能解决你的问题,请参考以下文章