只有一个常量值的表?

S. *_*ter 3 database-design sql-server entity-framework c# sql-server-localdb

我首先使用 C# 实体框架代码。

我对数据库的了解几乎一无所有。我不知道这是常见的还是好的做法,所以我想问你们我应该怎么做。

假设我有一张字母表和一张车站表。

我开发了一个应用程序,可以将信件从一个站发送到其他站。每封信都可以发送到不同的站点——这是一种多对多的关系。

发送的每封信都与一个站相关联 - 信的源站(信不能发送到它自己的源站,等等)。

每个数据库实例代表一个站。

在应用程序启动时,我知道哪个站是我的源站,并希望将该信息保存在数据库中。

我应该如何保存哪个站是源站?我是否应该在每一行的站台表中都有一个标志,指示它是否是当前实例站?这对我来说听起来很糟糕。

有没有办法让表格只有 1 个值?例如,它将被称为 InstanceStation,它将只包含一行和一列 - StationId?这是一个好习惯吗?

我尽量说清楚,希望我的情况清楚。

Han*_*non 6

Stations假设你没有十亿个电台,在桌子上有一个标志,表明哪个电台是本地电台,这正是我处理这个问题的方式。我可能会调用该列IsSourceStation或其他东西,并将其设为一个BIT可以接受NULL. 我会将本地站行标记为1,并将所有其他行保留为NULL因为这不会占用任何空间(请参阅下面关于空间的评论)。

我会向该IsSourceStation列添加一个过滤索引,过滤为WHERE IsSourceStation = 1. 如果需要,该索引将允许极快的查找来确定本地电台的名称。

查找Stations与我们的“home”站相对应的行可以通过以下方式完成:

SELECT *
FROM Stations
WHERE IsSourceStation = 1;
Run Code Online (Sandbox Code Playgroud)

无论Stations表中有多少行,我建议的索引都会非常快。

想确认一个站不是本站?用这个:

IF EXISTS (
    SELECT 1
    FROM Stations
    WHERE IsSourceStation IS NULL
        AND StationID = 1234
    )
BEGIN
    -- StationID 1234 is NOT the home station
END
Run Code Online (Sandbox Code Playgroud)

SQL Server 中使用的空位图是一种奇妙的优化,专为这种类型的情况而设计,即可空列中很少有行实际包含值。

我的发言上面,而技术上是正确的,该空位图用来节省空间,在以单个比特列的表的情况下,存在定义列可为空VS有它不为空,用之间没有明显差异默认值为 0。我使用以下测试台在 SQL Server 2012 上确定这一点:

USE tempdb;
IF EXISTS (SELECT 1 FROM sys.tables t WHERE t.name = 'TestBit')
DROP TABLE dbo.TestBit;
IF EXISTS (SELECT 1 FROM sys.tables t WHERE t.name = 'TestBitNotNull')
DROP TABLE dbo.TestBitNotNull;

CREATE TABLE dbo.TestBit
(
    TestBitID INT NOT NULL
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
    , IsBit BIT NULL
);

INSERT INTO dbo.TestBit (IsBit)
SELECT TOP(1000000) NULL
FROM sys.objects o1
    , sys.objects o2
    , sys.objects o3
    , sys.objects o4;


CREATE TABLE dbo.TestBitNotNull
(
    TestBitID INT NOT NULL
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
    , IsBit BIT NOT NULL
        CONSTRAINT DF_TestBitNotNull
        DEFAULT ((0))
);

INSERT INTO dbo.TestBitNotNull(IsBit)
SELECT TOP(1000000) 0
FROM sys.objects o1
    , sys.objects o2
    , sys.objects o3
    , sys.objects o4;
Run Code Online (Sandbox Code Playgroud)

上面的代码创建了两个表,每个表都有一个INT列和一个BIT列。第一个表允许BIT列是NULL; 第二个表没有。

我使用以下命令检查每个表第一页的实际磁盘数据页:

SELECT TOP(10) *
    , %%PHYSLOC%%
FROM dbo.TestBit
    CROSS APPLY fn_PhysLocCracker(%%PHYSLOC%%);

SELECT TOP(10) *
    , %%PHYSLOC%%
FROM dbo.TestBitNotNull
    CROSS APPLY fn_PhysLocCracker(%%PHYSLOC%%);
Run Code Online (Sandbox Code Playgroud)

DBCC PAGE命令可以与最后一个选项设置为“3”一起使用,以查看存储在页面上的实际列值,以及有关每行“槽”的相当多的详细信息。在我上面的两个表格中,每个表格的第一页分别是 334 和 342。

DBCC PAGE (2, 1, 334, 3) WITH TABLERESULTS;

DBCC PAGE (2, 1, 342, 3) WITH TABLERESULTS;
Run Code Online (Sandbox Code Playgroud)

上面每个 DBCC PAGE 命令的插槽 0 的输出显示了具有可为空列的表的以下内容:

在此处输入图片说明

而这个,对于不可为空的列:

在此处输入图片说明

第一行第二列的“内存转储”值以十六进制格式显示存储在磁盘上的实际数据;两种变体完全相同。

事实上,当使用这个查询查看两个表的磁盘大小时:

SELECT o.name
    , i.name
    , p.partition_number
    , p.rows
    , UsedMB = au.used_pages / 8192E0
    , TotalMB = au.total_pages / 8192E0
    , AvgRowsPerPage = p.rows / CONVERT(DECIMAL(10,2), au.used_pages) 
FROM sys.allocation_units au WITH (NOLOCK)
    INNER JOIN sys.partitions p WITH (NOLOCK) ON ((au.type = 1 OR au.type = 3) AND au.container_id = p.hobt_id) OR (au.type = 2 AND au.container_id = p.partition_id)
    INNER JOIN sys.indexes i ON p.object_id = i.object_id AND p.index_id = i.index_id
    INNER JOIN sys.objects o WITH (NOLOCK) ON p.object_id = o.object_id
    INNER JOIN sys.schemas s WITH (NOLOCK) ON o.schema_id = s.schema_id
WHERE o.name = 'TestBit'
    OR o.name = 'TestBitNotNull'
ORDER BY o.name;
Run Code Online (Sandbox Code Playgroud)

我们看到两个表的大小相同:

在此处输入图片说明

根据上述数据,我的结论是,仅使用默认值为 0 的不可为空列可能更容易,因为这消除了可空列所需的潜在有问题的空值处理。

当您有超过 8 个可空位字段时,空位图确实有帮助。如果您使用我的示例表TestBitTestBitNotNull给它们每个 16 位字段,您将看到以下 1,000,000 行的表大小:

在此处输入图片说明