简体   繁体   English

SQL Server 2005 FOREIGN KEY引用第二列的“常量”值

[英]SQL Server 2005 FOREIGN KEY that references a “constant” value for a second column

I have a table that has a foreign key to another table. 我有一个表,该表具有另一个表的外键。
For example: Postion.day REFERENCES weekdays.day which is fine. 例如: Postion.day REFERENCES weekdays.day很好。 However Position.day can hold weekdays where rdo=true . 但是Position.day可以保留rdo=true工作日。

The primary way of accessing this data is planned to be through a Web Application, that I'm witting. 我打算通过Web应用程序来访问这种数据的主要方式。 I plan on adding this check in the web-application anyway. 无论如何,我计划在Web应用程序中添加此检查。 I'm just looking for way to enforce as much data integrity at the DB level as I can short of writing triggers. 我只是在寻找可以在数据库级别上实现尽可能多的数据完整性的方法,而我还没有编写触发器。

I suspect the answer to look something like: 我怀疑答案看起来像这样:

ALTER TABLE Postition ADD COLUMN day CHAR(3)
            FOREIGN KEY REFERENCES weekday(shortName) 
            CHECK (weekday.rdo=TRUE);

Normally I would "try it and see", however, I have a lot of changes to make and I'm still finalizing my design and thought I would ask the experts and see what they had to say while I worked on the rest of it. 通常,我会“尝试一下然后看”,但是,我需要做出很多更改,并且我仍在完成设计,并认为我会请专家们看看他们在处理其余部分时要说些什么。 。

UPDATE: 更新:
ok So I have a table, Name Table (I didn't name it), I have another Table weekdays which lists All 7 days of the week along with some other info. 好的,所以我有一个表, Name Table (我没有给它命名),还有另一个表工作日,其中列出了一周中的所有7天以及其他一些信息。 Name Table has 2 Foreign Keys rdo and shortDay. 名称表具有2个外键rdo和shortDay。 weekdays holds a bit field for rdo and and a bit field for shortday stating if the day is eligible to used for those days. 工作日保留rdo和bitday的一个字段,说明该日期是否适合那些日子。 So I want my RDO field to be a foreign key to weekdays but ONLY WHERE RDO=TRUE. 因此,我希望我的RDO字段成为工作日的外键,但仅在RDO = TRUE的地方。

Weekdays primary Key is shortname, 3 letters( char(3) ) representing a weekday, EG: Mon, Tue, Wed, Thu, etc 工作日的主键是简称,代表工作日的3个字母(char(3)),例如:星期一,星期二,星期三,星期四等

I was thinking about it and remembered SQL transactions.(I'm currently betting that SQL Server will be smart enough to rollback a successful ALTER TABLE call, the position table already exists.) 我正在考虑它并想起了SQL事务。(我目前认为SQL Server足够聪明,可以回滚成功的ALTER TABLE调用,位置表已经存在。)

BEGIN
ALTER TABLE [Name Table] ADD RDO CHAR(3);
ALTER TABLE [Name Table] ADD FOREIGN KEY (RDO) REFERENCES weekdays(shortName);
ALTER TABLE [Name Table] ADD CHECK (TRUE=(SELECT rdo FROM weekdays 
                                                     WHERE shortName=RDO));
ROLLBACK;

Which returns from the Database: 从数据库返回的内容:

Error code 102, SQL state S0001: Incorrect syntax near ')'. 错误代码102,SQL状态S0001:“)”附近的语法不正确。
Line 1, column 1 第1行,第1列

Error code 1769, SQL state S0001: Foreign key 'RDO' references invalid column 'RDO' in referencing table 'Name Table'. 错误代码1769,SQL状态S0001:外键'RDO'引用了引用表'Name Table'中的无效列'RDO'。
Line 3, column 1 第3行,第1列

Error code 1046, SQL state S0001: Subqueries are not allowed in this context. 错误代码1046,SQL状态S0001:在此上下文中不允许子查询。 Only scalar expressions are allowed. 仅允许标量表达式。
Line 4, column 1 第4行,第1列

Error code 3903, SQL state S0001: The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION. 错误代码3903,SQL状态S0001:ROLLBACK TRANSACTION请求没有相应的BEGIN TRANSACTION。
Line 5, column 1 第5行,第1列

So Adding the foreign Key is easy enough but I'm still stumped on how to reference the linked date in another table inside of a check constraint. 因此,添加外键很容易,但是我仍然对如何在检查约束中引用另一个表中的链接日期感到困惑。

Ideally the Syntax would look like this(which I know is invalid): 理想情况下,语法应如下所示(我知道这是无效的):

ALTER TABLE [Name Table] ADD RDO CHAR(3)
            FOREIGN KEY REFERENCES weekdays(shortName,rdo=true);

does that help? 有帮助吗?

CHECK constraints are generally designed to work against the contents of a single row. CHECK约束通常设计为针对单个行的内容。 That's why SELECT statements aren't allowed. 这就是为什么不允许SELECT语句的原因。 If you're trying to determine whether the value stored in the column "day" is one of the weekdays, I'd do one of these things. 如果您要确定存储在“天”列中的值是否为工作日之一,则可以执行以下操作之一。

If the table "weekday" contains only the weekdays {'Mon', 'Tue', 'Wed', 'Thu', 'Fri'}, then all you need is a foreign key. 如果表“工作日”仅包含工作日{'Mon','Tue','Wed','Thu','Fri'},那么您只需要一个外键即可。

If the table "weekday" contains anything besides the weekdays {'Mon', 'Tue', 'Wed', 'Thu', 'Fri'}, I'd consider creating a different table of the five actual weekdays. 如果表“工作日”除工作日{'Mon','Tue','Wed','Thu','Fri'}之外还包含其他内容,我将考虑创建五个实际工作日的不同表。 Use whatever integrity constraints you think are necessary. 使用您认为必要的任何完整性约束。 The table "Position" can set a foreign key reference to that five-row table. 表“位置”可以为该五行表设置外键引用。

I might consider writing the entire constraint as a CHECK constraint. 我可能会考虑将整个约束写为CHECK约束。

ALTER TABLE Postition 
ADD COLUMN day CHAR(3)
CHECK (day IN ('Mon', 'Tue', 'Wed', 'Thu', 'Fri');

But I probably wouldn't do that. 但是我可能不会那样做。 The advantage of having weekdays in a table is that you can use the table in an outer join to provide full weeks, even when some days might have no data. 在表中具有工作日的好处是,即使某些天可能没有数据,也可以在外部联接中使用该表来提供整周的时间。 (But not every application needs that.) (但并非每个应用程序都需要。)

Later . 以后。 . .

To set a foreign key constraint to only those rows where RDO=TRUE , you need to identify those days as rows in a table. 要将外键约束仅设置为RDO=TRUE那些行,您需要将这些天标识为表中的行。 (Foreign keys target a column, not a column filtered by a value in another column.) Right now you have two Boolean flags. (外键以一列为目标,而不是以另一列中的值过滤的列为目标。)现在,您有两个布尔标志。 Consider creating two tables instead. 考虑改为创建两个表。

create table rdo (
  day char(3) primary key references weekdays (shortname)
);
insert into rdo values ('Mon');
insert into rdo values ('Tue');
insert into rdo values ('Wed');
insert into rdo values ('Thu');
insert into rdo values ('Fri');

create table shortday (
  day char(3) primary key references weekdays (shortname)
);
insert into shortday values ('Fri');

You can set foreign key references to either of those tables. 您可以为任何一个表设置外键引用。

You'll get better answers if you provide DDL and sample data as SQL INSERT statements. 如果提供DDL和示例数据作为SQL INSERT语句,则将获得更好的答案。

The way I'm currently working to achieve this is: 我目前正在努力实现这一目标的方法是:

  • Add an extra bit column to Table , called eg IsRdo . Table添加一个额外的bit ,例如IsRdo
    • Add a check that Table.IsRdo = true . 添加一个check Table.IsRdo = true Yes, we have a column that's always 1 . 是的,我们的列始终为1
    • Or, I've just noticed, make it a constant: [IsRdo] AS cast(1 as bit) PERSISTED 或者,我刚刚注意到,将其设置为常量: [IsRdo] AS cast(1 as bit) PERSISTED不变
  • Add a composite candidate key on weekdays consisting of (shortName, rdo) . weekdays添加一个由(shortName, rdo)组成的复合候选密钥。
  • Make the foreign key from Table to weekdays match (shortName, rdo) . 使Table的外键与weekdays匹配(shortName, rdo)

This ensures that a Table row can only correspond to a weekdays row where rdo is true . 这样可以确保“ Table行只能对应于rdotrueweekdays行。

This seems a bit odd (again, we have a column that's always 1 ) but I've not found a better way to do it. 这似乎有点奇怪(再次,我们的列始终为1 ),但我还没有找到更好的方法。

If it's really as simple as the days of the week then I would consider using a simple CHECK constraint on the hard-coded values for the weekdays. 如果它真的和一周中的某几天一样简单,那么我将考虑对工作日的硬编码值使用简单的CHECK约束。 I'm more adamant than most against hard-coding things, but It's not like the days of the week are likely to change any time soon. 我比大多数人都更坚决反对硬编码,但是这似乎并不意味着一周中的日子很快就会改变。

I thought that an FK on a filtered index (SQL 2008) or on a materialized view might solve the problem, but apparently neither of those approaches work :( 我认为过滤索引(SQL 2008)或实例化视图上的FK可能会解决问题,但显然,这些方法都不起作用:(

If you have control over the entire process (the code that calls the database and the database itself), then I would strongly recommend that you use stored procedures to perform your insert/update/deletes. 如果您可以控制整个过程(调用数据库和数据库本身的代码),那么我强烈建议您使用存储过程来执行插入/更新/删除操作。

You would then implement your validations within the stored procedure prior to performing the actions and raiserror with the appropriate errors when you find an issue. 然后,您将在执行操作之前在存储过程中实施验证,并在发现问题时提出适当的错误。

The primary benefit are 主要好处是

1) Your business rules are encapsulated and easy to understand when it it is time to change them in 6 months or a year 1)您的业务规则已封装并且易于理解,何时需要在6个月或一年内进行更改

2) You can provide a much more meaningful error message to your users than that provide when a check constraint or other constraint is encountered. 2)您可以向用户提供比遇到检查约束或其他约束时更有意义的错误消息。 Users often get very confused when they see a strange exception. 当用户看到奇怪的异常时,通常会感到非常困惑。

In fact, because of item #2, we have removed all unique constraints from our tables and replaced them with equivalent stored procedure functionality that can tell the user exactly what is wrong and what they need to do to resolve the issue. 实际上,由于项目#2,我们从表中删除了所有唯一约束,并用等效的存储过程功能替换了它们,这些功能可以告诉用户确切的问题以及解决问题所需的操作。 Support calls have dropped significantly as a result. 结果,支持电话大大减少了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM