一个数据库,多个客户

3
我正在编写一个产品,该产品只有一个数据库,但将通过使用客户ID的全局标识符来托管许多客户端。
这一切都很好。
然而,假设我们有一个名为Ticket的表格。Idea具有一个主键。当用户添加票证时,票证将通过外键等与客户对应。
然而,对于每个客户,我希望他们的票证编号在注册时从1开始。
例如:客户1添加4张票证,则票证编号计数为4。客户2注册后,他们添加了一张票证,然后票证编号将是5,依此类推。这并不理想。
我的问题是,如果我不想使用多个数据库,我该如何解决这个问题?每当我进行更改时,必须更新数百个数据库以添加新列、索引等,这种情况会让我非常烦恼。
希望这样说得清楚,期待您的回答。
编辑:由于我将在Symfony中使用Doctrine ORM来管理数据库,因此已将其标记为Symfony...可能无关紧要,但仍然添加。
编辑:我也可能很愚蠢,忽略了一些明显的东西,所以请谅解。

对我来说,有些地方不太清楚。当你说“客户2注册,他们添加一张票,然后customer_id将为5,依此类推”时,你是指ticket_id将为5吗?我认为,每个客户的ticket_id递增是可以的。这样你需要支持的项目就会少得多。如果你想创建一个由customer_id和ticket_id组成的复合键,其中ticket_id的最大值是另一个客户的FK,则可以这样做。但是,我认为只要让ticket_id递增而不管客户是最好的主意。 - bogertron
抱歉,我意识到自己犯了错误并已进行更正(实际上应该说ticket_id)。同意,只有ticket_id会更简单易操作,但是我不希望客户认为“咦?我的票号是4341吗?我只创建了2张票?”或者你认为这不是个问题吗?此外,我对其他公司如何管理类似的集中式数据库以及如何按客户保持连续ID等方面感到好奇。 - Flukey
这应该不是什么大问题,或者你看着SO上的ID会想:“这个问题3112050是我自己在PHP中一般情况下提出的吗...” ;)。如果你真的想移动ID,你可以创建“漂亮的URL”来隐藏ID,并用票据标题或其他内容替换它。 - DrColossos
如果您正在使用Doctrine,那么您应该使用此标签,而不是Symfony。 - jigfox
谢谢stackoverflow!像往常一样,你们再次证明了自己是无价之宝。我只希望我能将多个答案标记为已接受的解决方案! - Flukey
显示剩余2条评论
7个回答

3
为什么不添加一个ticket_num列,自己管理按客户ID顺序排列的序列?如果您正在使用Doctrine,请查看在Ticket模型中实现preInsert方法。

因为如果我删除一张票,那么顺序就会被搞乱。:-( 除非我使用软删除... - Flukey
@Jamie -- 你为什么要删除一个工单?只需关闭它,将其标记为已过时等等。无论如何计算ID,保持数字同步的问题都将存在。 - MJB
1
我肯定会在类似于票务系统这样的项目中使用软删除,可以使用Doctrine Behavior或者仅仅实现一些“已删除”的状态来处理。 - timdev
@MJB - 啊,是的,这非常正确。票证只是一个例子,我也可以使用用户作为例子。例如,用户ID 1表示客户1,用户ID 1表示客户2。而且我可能会从系统中删除一个用户。也许我应该停止担心最终用户在URL等方面看到什么。 - Flukey
@Jamie 可以使用软删除,或者在客户表中创建一个计数器列,该列针对每个新工单递增,但不会因删除工单而递减。 - jigfox
请勿删除票据,按照指示使用软删除。如果您担心用户在URL方面会看到什么,请创建URL重写规则或使用UUID进行票证查找,或者使用其他加密措施。保留票证有助于您识别客户购买、遇到问题或其他相关情况。 - Don

2
我能理解每个客户都会按顺序列出他们的票吗?我假设每个客户都有自己的票,这些票不会被其他客户看到。你应该在问题中澄清这一点。
我建议在您的票务表中创建一个新的列作为参考编号(我们称之为ref_num)。当客户2创建一张票时,票号将是5,但是您的应用程序将知道为该用户分配ref_num为1。
为了使其正常工作,您需要找到一种跟踪每个客户创建的票数的方法。也许对于每个联系人,您可以有一个名为ticket_count的列,每次增加一个。这样,从您的应用程序中,您只需拉取此数字并将其增加一以更新计数并获取新的票号。
FYI,在这里,我们不担心向用户隐藏信息。我们使用ref_num字段来隐藏真实的ID号码。我们有多个部门,每个部门有多个客户,共享相同的ID池。如果A组创建了23456号票,则B组创建另一张票,尽管上一张票不属于他们,但它显示为23457。票号不需要是连续的。

你是正确的,“每个客户都有自己的票,这些票不会被其他客户看到。”你的想法似乎很可行,但存在一个问题。如果由于某种原因数据库出现故障,并且我得到了一个奇怪的票号计数,然后将其添加到票ref_num中,那么我将陷入困境。只是考虑最坏的情况。 - Flukey
不必存储票数,您可以使用一个查询来计数之前为该用户存在的票数,然后将该值增加一并插入数据库中。这需要在数据库上进行额外的查询...虽然根据应用程序的大小、使用频率和用户数量可能不重要。 - ravibhagw
2
不要计数,获取最大值并递增。SELECT MAX(ref_num) FROM tickets WHERE client_id=? 如果票据永远不会被销毁,那么这应该是等效的,但最好使用MAX()来确保您永远不会重复。 - timdev

2
另一个可能性是不使用连续的整数票证编号。你可以给票证加上时间戳,甚至可以精确到秒,然后使用它。我与某些供应商合作时就是这样做的。其他人会跟踪计数并将其附加到时间戳上,这看起来像是一个没有间隙的唯一号码,但实际上可能并非如此。
在这种情况下,客户会看到一个类似于“2010062407”的票证号码,并认为:“显然与日期有关,甚至可能与时间有关”。但我不认为客户会说:“我怎么会积累了20亿个故障单?!”

2

我认为您可能混淆了主键和业务键的概念。主键的作用是唯一标识表中的一行,没有其他作用。如果试图将某种含义附加到主键上,则初始设计可能存在问题。

如果票号具有某些业务含义,并且期望按顺序排列,则为每个客户设置一个序列生成器并添加一列。


1
创建一个名为CustomerTickets的新表,其中包含两列:客户ID和工单ID。 对于您的客户,请从表中读取最后一个工单ID,并将其递增,然后将其在事务中写入您的实际工单表中。

1

我建议你保持主键ID不变,从逻辑上讲它应该是内部使用的。你可以生成一个唯一标识符(GUID、时间戳)并用于显示目的。或者,你可以生成一个唯一的ID(用于显示目的),将客户名称的前4个字母组合起来,然后用0进行填充。

类似这样:

APPL000006

GOOG000004

使用这种方法,去掉前四个字母,你就可以得到你的主键ID了。


0
你也可以尝试为每个客户创建额外的表,这样你就会得到每个客户的新表。
例如:表格:
tickets_1 //all tickets for customer with id 1
tickets_3 //all tickets for customer with id 3

(如果你没有成千上万的客户,这应该不是问题。)


这对我来说听起来有问题。每个客户的每张表都需要一个新版本,不是吗?所以10个客户意味着10个票据表,10个订单表,10个rma表等等?那为什么不在这种情况下为每个客户拥有一个单独的数据库或模式呢? - MJB
同意MJB的观点,如果你是这个数据库的管理员,我不想切换 ;) - DrColossos
因为问题中引用的话:“我目前正在编写一个只有一个数据库但将托管许多客户端的产品”,所以这并不像使用多个数据库那么容易,但是这是可能的。 - TheHippo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接