复合主键/外键问题

时间:2011-05-06 20:51:30

标签: c# asp.net sql-server-2008 foreign-keys composite-primary-key

我正在使用SQL Server 2008 R2,C#和ASP.Net。我有一个表,其中包含一个复合主键,该主键由一个票号和该票证在表中显示的次数组成。票证频率由C#代码计算:

VTTTickets.InsertParameters[0].DefaultValue = VTTTTicketNoBox.Text;
string CommString = "SELECT COUNT(*) FROM [Tickets] WHERE [Ticket_No] = " + 
VTTTTicketNoBox.Text;
string ConnString = ConfigurationManager.ConnectionStrings[1].ConnectionString;
OdbcConnection Conn = new OdbcConnection(ConnString);
Conn.Open();
OdbcCommand FooCommand = newOdbcCommand(CommString,Conn);
int FooVal = Convert.ToInt32(FooCommand.ExecuteScalar()) + 1;
VTTTickets.InsertParameters[1].DefaultValue = Convert.ToString(FooVal);
VTTTTicketNoBox.Text = "";
Conn.Close();

我的表约束/ etc代码

CONSTRAINT [PK_Tickets] PRIMARY KEY CLUSTERED 
([Ticket_No] ASC, [Ticket_Sub_No] ASC)
WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = ON,
ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY],
CONSTRAINT [Unique_Ticket_No] UNIQUE NONCLUSTERED 
([Ticket_No] ASC)
WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = ON,     
ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY],
) ON [PRIMARY]
GO

唯一约束的原因是因为主复合键的该部分也是另外两个表的外键。 我不断得到的错误是当它因为在唯一约束列中存在重复值而向我大喊大叫时。一旦所有内容从开发转移到生产,这可能会发生,因为可能会重新提交票证。

有没有办法在没有唯一键约束的情况下制作外键? 如果没有,我该如何解决这个问题?

1 个答案:

答案 0 :(得分:4)

这是我们在编程中经常遇到的一种情况。 Technology_A不会让我做Operation_B,但它应该!然后,在将我们的头撞到墙上一段时间后,我们放弃(或前往Stackoverflow)。我们这样做是因为我们没有按照预期使用该技术(这是好的,这就是我们学习的方式!)

这里的问题是您的数据库架构。

你试图用一张桌子做太多。您不应该在跟踪出现次数的同一个表中存储票证(同一票证可能存在多次)。

在我看来,有两个很好的策略来纠正这种情况。首先,我将创建一个带有单列主键的票证表。然后,我将创建一个存储票证的每个实例的表。

enter image description here

示例:Ticket43已关闭,但重新打开并再次关闭。这意味着(如果我正确地读了你的问题),票证有两个实例。这意味着它在原始表中将有两个条目,但在我新建议的模式中,它将在Tickets中有一个条目,在Ticket_Instances中有两个条目。

注意:您需要确保存储Tickets中实例之间永远不会更改的故障单信息,以及Ticket_Instances中特定于实例的故障单信息。

要记录Ticket Counts ,我要做的第一件事就是编写一个SQL或一些SQL视图,如下所示:

SELECT
  count(*) as TicketCount,
  TicketID
FROM
  Ticket_Instances as TI
GROUP BY
  TicketID

如果您不想按需计算,那么我建议您使用:

  • ASP.NET缓存(运行上述SQL,将其阻塞到缓存中,TTL为10分钟)
  • 使用由触发器填充的Ticket_Counts表

我怀疑你会更喜欢那里的选项2(虽然我会使用选项1)。

触发方法:

假设Ticket_Instances可能永远不会被删除,您只需要一个插入触发器。您将在Insert上的Ticket_Instances表上创建一个触发器,该触发器SQL将执行以下操作:

  • 如果Ticket_Counts中不存在TicketID,则将TicketID插入TicketCount为0的Ticket_Counts
  • 然后,将TicketCount增加1 Ticket_Counts中的TicketID

使用此方法,您只需要通过TicketID访问Ticket_Counts即可获取该特定故障单的出现次数。

我认为一旦更新架构,您会发现约束错误消失了。