S. *_*ter 3 database-design sql-server entity-framework c# sql-server-localdb
我首先使用 C# 实体框架代码。
我对数据库的了解几乎一无所有。我不知道这是常见的还是好的做法,所以我想问你们我应该怎么做。
假设我有一张字母表和一张车站表。
我开发了一个应用程序,可以将信件从一个站发送到其他站。每封信都可以发送到不同的站点——这是一种多对多的关系。
发送的每封信都与一个站相关联 - 信的源站(信不能发送到它自己的源站,等等)。
每个数据库实例代表一个站。
在应用程序启动时,我知道哪个站是我的源站,并希望将该信息保存在数据库中。
我应该如何保存哪个站是源站?我是否应该在每一行的站台表中都有一个标志,指示它是否是当前实例站?这对我来说听起来很糟糕。
有没有办法让表格只有 1 个值?例如,它将被称为 InstanceStation,它将只包含一行和一列 - StationId?这是一个好习惯吗?
我尽量说清楚,希望我的情况清楚。
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 个可空位字段时,空位图确实有帮助。如果您使用我的示例表TestBit并TestBitNotNull给它们每个 16 位字段,您将看到以下 1,000,000 行的表大小:
| 归档时间: |
|
| 查看次数: |
1192 次 |
| 最近记录: |