【问题标题】:Storing detailed data in SQL Server在 SQL Server 中存储详细数据
【发布时间】:2018-02-26 20:51:32
【问题描述】:

我正在设计一个用于保存选票的数据库。 我创建了一个表:

CREATE TABLE [dbo].[users_votes](
    [id] [bigint] NOT NULL,
    [like_votes] [int] NOT NULL DEFAULT ((0)),
    [dislike_votes] [int] NOT NULL DEFAULT ((0)),
    [commented_votes] [int] NOT NULL DEFAULT ((0)),
    [comments_likes] [int] NOT NULL DEFAULT ((0))

问题是还需要按位置存储细分数据。 因此,例如,如果 user_votes 表有 1,000 个 like_votes 用于特定 id,我需要知道按位置细分,例如:

美国 340

法国 155

丹麦 25

巴西 290

澳大利亚 190

我以逗号分隔的字符串形式从客户端获取数据,例如: (1,2,45,67,87,112,234) 和国家/地区代码(us、au、ca 等)。

我一直在考虑存储这些数据的几种可能性,但想知道哪种方法最适合(如果有的话)。

  1. 由于国家代码的数量是有限的,我可以扩展users_votes 表并为每个条件添加包含国家代码的列。例如。 like_votes_us, dislike_votes_us, comment_votes_us, comment_likes_us. 在这种情况下,我可能会使用动态 SQL 来插入/更新数据。

  2. 为每一列创建新表,例如,我将有一个名为like_votes 的表,其中我将有一个id, external_id,它将是users_votes(表)id、country_codecount 列.因此数据将存储在users_voteslike_votes 表中。我将记录external_idcountry code 的每个组合。 在这种情况下,我将需要迭代插入的数据以确定此 external_id 组合是否存在(然后只是增加它)还是需要插入。

哪种方法(如果有)是存储这些数据的最佳方法,以便于插入/更新和查询?

【问题讨论】:

  • 除非我弄错了,否则您是否将汇总值存储在表中?为什么?考虑到您的目标是什么,这不是一个好主意。您是否考虑过使用范式?
  • 你指的是users_votes表吗?如果是这样,那么是的。
  • 这是您目前唯一的桌子。 :) 好吧,如果是这样的话,老实说,你的设计似乎在片场就有缺陷。给我一点时间来发布一些可以帮助你有一个好的开始的东西。

标签: sql-server database-design


【解决方案1】:

老实说,您目前拥有的这种类型的桌子设计并不是一个好主意。构建良好的关系数据库的一大重点是使用范式。我不打算解释这里是什么,因为互联网上有成千上万的文章解释它,以及它的不同迭代(从 1NF 到 6NF iirc)。

无论如何,您可以使用几张桌子轻松完成此操作。我不得不猜测你在这里的很多设置,但希望你能够推断出你需要什么,并调整不需要的东西。

首先,让我们从一个客户表开始:

CREATE TABLE dbo.Client (ClientID int IDENTITY(1,1),
                         ClientName varchar(100), --You should really split this into Title, Forename and Surname, I'm just being "lazy" here
                         ClientCountryID int, --Not sure if a Client is related to a country or the vote is, i've guessed the client is.
                         DOB date,
                         EmailAddress varchar(100));
GO

所以,我们现在有一个简单的客户表。接下来,我们需要一个国家表。这很简单:

CREATE TABLE dbo.Country (CountryID int IDENTITY(1,1),
                          CountryName varchar(100),
                          CountryCode char(2)); --For example UK for United Kingdom, FR for France, etc
GO

您可能想在此处存储其他内容,但我不知道您的设置。

现在,这就是我真正猜测的地方。我假设您的好恶等与某事有关。什么,我不知道,所以,我将有一个名为“内容”的表格,但是,不知道这些喜欢反对什么,我没有这个表格的上下文,因此它将是非常基本的:

CREATE TABLE dbo.Content (ContentID int IDENTITY(1,1),
                          ContentType int, --Guessing might be types, maybe videos, Comments, articles? I have no idea to be honest)
                          ContentParent int, --Comments are joined to a Content (just like here on SO)? I'll guess it's possible
                          Content nvarchar(MAX)); --because I have no idea what's going in there

--Very simple Content Type Table
CREATE TABLE dbo.ContentType (TypeID int IDENTITY(1,1),
                              TypeDescription varchar(100));
GO

现在,我们终于可以获取您想要存储的选票了;可能看起来像这样:

CREATE TABLE dbo.Vote (VoteID int IDENTITY(1,1),
                       ClientID int,
                       ContentID int,
                       Liked bit); --1 for Liked, 0 for Disliked, NULL for N/A perhaps?
GO

好的,现在我们有一些桌子。现在我意识到我没有提供任何类型的 Sample 数据进入这里,所以我将为您提供一些 INSERTS 语句,以便您了解:

INSERT INTO dbo.Country (CountryName, CountryCode)
VALUES ('United Kingdom','GB'),
       ('France','FR'),
       ('Germany','DE');
GO

INSERT INTO dbo.Client (ClientName, ClientCountryID, DOB, EmailAddress)
VALUES ('Mr John Smith',1, '19880106','Bob@gmial.com'),
       ('Ms Penelope Vert',2,'19930509','PVert@mfn.com');
GO
INSERT INTO dbo.ContentType (TypeDescription)
VALUES ('Video'),('Article'),('Comment');
GO
INSERT INTO dbo.Content (ContentType, ContentParent, Content)
VALUES (2, NULL, 'This is my first article, hi everyone!'),
       (3, 1, 'Nice! Good to see you''re finally posting!'),
       (1, NULL, 'http://youtube.com');
GO

--And now some votes:
INSERT INTO dbo.Vote (ClientID, ContentID, Liked)
VALUES (1, 1, 1),
       (2, 1, 1),
       (2, 2, 1),
       (2, 3, 0);
GO

请注意我是如何输入投票的。我没有在表格中汇总;这样做是一个糟糕的主意。而是单独存储每个投票并使用查询来聚合。您可以轻松做到这一点,例如:

SELECT C.ContentID,
       Cy.CountryName,
       COUNT(CASE V.Liked WHEN 1 THEN 1 END) AS LikedVotes,
       COUNT(CASE V.Liked WHEN 0 THEN 1 END) AS DisLikedVotes
FROM dbo.Content C
     JOIN dbo.Vote V ON C.ContentID = V.ContentID
     JOIN dbo.Client CV ON V.ClientID = CV.ClientID
     JOIN dbo.Country Cy ON CV.ClientCountryID = Cy.CountryID
GROUP BY C.ContentID,
         Cy.CountryName;

这会为您提供每个内容项目的点赞数,并为您将其划分为国家/地区。如果您想将这些国家/地区放入自己的列中,那么我强烈建议在您的表示层中执行此操作,而不是您的 SQL(因为您必须使用动态 SQL,并且(不冒犯)我想象一下,根据您当前的数据库设计选择,这超出了您目前的技能)。 Excel 非常擅长使用数据透视表。如果您想在 SQL Server 中保留该过程,请考虑使用 SSRS 和矩阵。

如果您有任何问题,请尽管提问。

注意:我在这里没有制作任何类型的外键、约束、默认值等。对于任何好的数据库设计来说,这些都是必不可少的。

清理脚本:

DROP TABLE dbo.Client;
DROP TABLE dbo.Country;
DROP TABLE dbo.Vote;
DROP TABLE dbo.Content;
DROP TABLE dbo.ContentType;
GO

【讨论】:

  • 感谢您的详细解答。如果我理解正确,您是在建议将每张选票存储在自己的记录中?不是很大吗?例如,如果我有 50K 项目和 200K 投票用户,那么表格大小将是 50K X 200K?
  • @UdiIdan ,是的,这正是我的建议。投票的每一行包含 3 x int 和 1 x bitint 的大小为 4 个字节,bit 的大小为 1 个字节。每行总共有 3 x 4 + 1 = 13 个字节。还有一个开销(我不记得它的大小,但我认为它可能是 4 个字节),所以总共 17 个字节。 1 兆字节是 1048576 字节。因此,在 1MB 的数据中,您可以存储大约 61680 张选票。对于非常小的存储空间,这是很多选票。所以,回答你的问题“它不会很大吗?”:不,不会。
  • 不,真的没有。考虑到您已经设计了表格,后来发现您需要获取投票人的国家/地区,是什么让您认为从长远来看这不会改变?如果你真的说你要存储 100 亿张选票,你真的认为这些数据会“小”吗?
  • 举个简单的例子,如果将来您需要知道客户性别的投票细节会怎样?如果您在投票和客户之间没有关系,而只是聚合表,您如何建议您获取该数据?你可能会说“但他们不会这么问”,但这并不意味着他们不会。在我自己的经验中,我曾无数次询问并被告知“不,我们不需要那个,也永远不需要它”被问到,也许一年后“我们能做到吗?”。然后,当我告诉他们不时,他们会非常生气;因为他们说他们永远不需要它。
  • 即使在您描述的规模下,这也是 100% 正确的方法,这对我来说似乎有点牵强。你真的希望你的 20 万用户中的每一个都投票 5 万次吗?这将要求每个用户每天投票 13 次,持续 10 年!这相当于您的系统每分钟收到 1902 票(每秒 30 票),持续 10 年。如果您有这样的互动客户群,那么您需要聘请专业的 DBA 尽快为您解决这些问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-10
  • 1970-01-01
  • 2021-12-24
  • 1970-01-01
相关资源
最近更新 更多