我有两个设计糟糕的表Form和FormDetails,我正在努力清理。
Form表包含有关我的应用程序中表单状态的信息:
+-----------------------------------------+
| form_id | status_id | form_created_by |
+-----------------------------------------+
| 1 | 1 | abc |
+-----------------------------------------+
| 2 | 3 | def |
+-----------------------------------------+
Form表中的form_id是主键。
FormDetails表包含有关表单的其他信息:
+-----------------------------------------+
| form_id | status_id | process_id |
+-----------------------------------------+
| 1 | 1 | 1 |
+-----------------------------------------+
| 2 | 2 | 1 |
+-----------------------------------------+
| 2 | 3 | 1 |
+-----------------------------------------+
| 2 | 3 | 1 |
+-----------------------------------------+
Form表中的form_id是 NOT 主键或外键。没有限制。此表设计不佳,随着时间的推移,重复数据已添加到此表中。
我想通过将唯一数据复制到新的FormDetails表中并使form_id成为Form表的外键来清理此表。
为了达到这个目的,我尝试了以下查询:
select *
from FormDetails fd
right join Form f on f.form_id = fd.form_id and f.status_id = fd.status_id
不幸的是,由于form_id = 2有两行且status_id = 3,我仍然会获得具有重复form_id的行。
+-----------------------------------------+
| form_id | status_id | process_id |
+-----------------------------------------+
| 1 | 1 | 1 |
+-----------------------------------------+
| 2 | 3 | 1 |
+-----------------------------------------+
| 2 | 3 | 1 |
+-----------------------------------------+
我想写的是一个查询:选择FormDetails中与当前Form相对应的所有行。如果有重复,只需选择其中一个。
知道怎么写这样的查询吗?
我希望看到的是:
+-----------------------------------------+
| form_id | status_id | process_id |
+-----------------------------------------+
| 1 | 1 | 1 |
+-----------------------------------------+
| 2 | 3 | 1 |
+-----------------------------------------+
答案 0 :(得分:0)
您可以使用ANSI SQL函数row_number()
枚举FormDetails
中每个form_id
的行。您不关心特定订单是什么,因此以下内容应该符合您的要求:
select form_id, status_id, process_id
from (select fd.*, row_number() over (partition by form_id order by form_id) as seqnum
from formdetails fd
) fd
where exists (select 1 from forms f where fd.form_id = f.form_id) and
seqnum = 1;
答案 1 :(得分:0)
使用SELECT DISTINCT声明
SELECT DISTINCT *
FROM FormDetails fd
RIGHT JOIN Form f ON f.form_id = fd.form_id AND f.status_id = fd.status_id
答案 2 :(得分:0)
在Distinct rows
表格中找到Form
,然后将Right Outer join
与Formdetails
表格一起使用
SELECT f.form_id,
f.status_id,
f.process_id,
FROM FormDetails fd
RIGHT JOIN (SELECT DISTINCT form_id,
status_id,
process_id,
FROM Form) f
ON f.form_id = fd.form_id
AND f.status_id = fd.status_id
答案 3 :(得分:0)
我会采取哪些措施来解决您的问题
1-创建一个新字段,作为formDetails表中的唯一键。
Alter Table formDetails Add formDetail_id int Not Null Identity(1,1) Constraint PKFormDetail Primary Key
2-然后我会创建一个临时字段,用于记录现有数据的评估。我的意思是创建的临时字段(我将其命名为Eval)可以包含来自3个字段的计算值(form_id,status_id和process_id)。
Alter Table formDetails Add Eval int
Update formDetails Set Eval = form_id * 100000 + status_id * 1000 + process_id
无论您采取何种解决方案,都必须注意结果不会包含"碰撞" (取决于表中包含的数据。我的意思是在这种情况下,process_id必须低于1000且status_id * 1000必须低于100000)。
3-删除不需要的行,如下所示:
Delete x
FROM ( select *, rn=row_number() over (partition by Eval order by formDetail_id)
from [dbo].[formDetails]) x
Where rn > 1;
(您可以在执行删除操作之前测试它执行select而不是删除以确保结果。)
4-从表中删除临时字段。
Alter Table formDetails Drop Column Eval
5-您现在可以直接在两个表的form_id字段之间设置外键。
Alter Table formDetails Add Constraint FKFormId Foreign Key (form_id) references forms(form_id)
答案 4 :(得分:0)
CREATE TABLE #Form
(
formId INT,
StatusID INT,
FormCreate VARCHAR(30)
)
INSERT INTO #Form VALUES(1,1,'abc')
INSERT INTO #Form VALUES(2,3,'def')
CREATE TABLE #Form1
(
formId INT,
StatusID INT,
ProcessID INT
)
INSERT INTO #Form1 VALUES(1,1,1)
INSERT INTO #Form1 VALUES(2,2,1)
INSERT INTO #Form1 VALUES(2,3,1)
INSERT INTO #Form1 VALUES(2,3,1)
SELECT DISTINCT * FROM #Form fd
LEFT JOIN #Form1 f ON f.formId = fd.formId AND f.StatusID = fd.StatusID
答案 5 :(得分:0)
在一些朋友的帮助下,我设法找到了这个问题的答案。我的问题最接近的答案是@Gordon Linoff,但它仍然不是我想要的。
为了确定Form表中有多少行在FormDetails表中没有任何相应的记录,我们编写了以下查询:
SELECT *
FROM Form
LEFT OUTER JOIN FormDetails ON Form.form_id = FormDetails.form_id
AND Form.status_id = FormDetails.status_id
WHERE
FormDetails.form_id IS NULL
当我在我的桌子上运行它时,它返回了38行。这个数字帮助我们确定我们写的查询是否返回了正确的行数。 @Gordon Linoff的回答是不同的26行所以我认为它返回的行多于预期,但它比所有其他答案更接近并朝着正确的方向迈出了一步。
然后我们把这个查询放在一起使用CROSS APPLY
(我之前从未见过的一些sql)。
查询如下所示:
SELECT ad.*
FROM Form f
CROSS APPLY (SELECT TOP 1 * FROM FormDetails fd WHERE
f.form_id = fd.form_id AND f.status_id = fd.status_id) AS ad
当我将Form表中的行数与此查询返回的行数进行比较时,得到的差值为38.这似乎表明这是解决我问题的一个很好的解决方案。
我不会想到使用CROSS APPLY来解决这个问题......但我想我会在这里记录解决方案,以防将来帮助某人。