0

原始问题

所以我没有创建的商业模型有一个可选的关系(就像在 ER 模型中一样)。自从我在 DB 工作以来已经有一段时间了,所以我可能会忘记一些事情。目前第一张表的FK(Foreign Key)指向第二张表的PK(Primary Key),是一个ID;我不记得这个术语,但它是“假”的,而不是 RDBMS(关系数据库管理系统)使用的“真实”。为简单起见,让我们假设只有 2 个表。

目前,当不需要可选关系时,我在 FK 列/属性中有空值。当该列中有一个项目时,我想要充分的优势,检查关系的另一端是否有匹配的项目,FK 指向(第二个表),也触发(虽然目前没有)和其他验证。直到不久前,当我意识到我不想在第一个表的重要部分上重复时,我都很满意,所以我想创建一个唯一键,但似乎无法创建一个键,其中包含一个列/属性可能包含 null。到目前为止,向我提出了 2 个解决方案,尽管我都不明白。

第一个是我设置默认值,基于数字的类型为 0,基于字符的类型为空字符串 ('')。我没有得到的是第二个表已经有一个具有相应值(0)的行/元组。如果我要将当前行转换为没有默认行,我假设我也会将相应的内容放入默认值,在我的情况下它是基于字符的类型。因此,启用索引的“成本”将是进行大量无用的连接,然后通过软件进行大量无用的合并,在我的例子中,是办公套件Apache OpenOffice Base的数据库部分。这似乎增加了很多处理,在我看来,某种触发器以及我当前的设计会更好。

第二个是制作一个“链接”表(他/她的任期),一个多对多的关系,但我认为这些仅适用于具有超过 1 个可能关系的条目;具有0-1关系的人不会使用它。无论如何,我仍然会遇到同样的问题,在那个“链接”表中不需要有一个条目。IIRC,该表的两个“边”必须包含有效的候选键。

因此,在业务模型确实需要该选项的情况下,已经实现了 1-1 关系,并且 FK 中的当前非空条目。现在我只需要为业务模型不需要可选部分的情况实现一个方法,以允许 0-1 关系,用于FK 中的当前空条目,同时不允许重复

感谢您的帮助

请求_

现在包含第三示例

以下小节包含使用命令从 Apache OpenOffice Base 导出的半 SQL 导出SCRIPT 'PATH\TO\NAME.sql'。原始文件及其导出和非导出查询位于如何使用可选 FK 创建索引?示例 3

我想要3 列/属性 ID_to_part1、model_number 和 ID_to_part2的唯一键;但是,在上一节中,原始问题显示 HSQLDB 版本 1.8.0.10 不允许在作为唯一键一部分的列中包含空值。

HSQLDB 导出

产生某种 SQL ;包括非标准陈述。

SET DATABASE COLLATION "Latin1_General"
CREATE SCHEMA PUBLIC AUTHORIZATION DBA
CREATE CACHED TABLE "Table1"("ID" INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 0) NOT NULL PRIMARY KEY,"ID_to_part1" INTEGER NOT NULL,"model_number" VARCHAR_IGNORECASE(3) NOT NULL,"ID_to_part2" INTEGER)
CREATE CACHED TABLE "Table2"("ID" INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 0) NOT NULL PRIMARY KEY,"content" VARCHAR_IGNORECASE(1) NOT NULL)
CREATE CACHED TABLE "Table3"("ID" INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 0) NOT NULL PRIMARY KEY,"content" VARCHAR_IGNORECASE(1) NOT NULL)
ALTER TABLE "Table1" ADD CONSTRAINT SYS_FK_87 FOREIGN KEY("ID_to_part1") REFERENCES "Table3"("ID") ON DELETE CASCADE ON UPDATE CASCADE
ALTER TABLE "Table1" ADD CONSTRAINT SYS_FK_90 FOREIGN KEY("ID_to_part2") REFERENCES "Table2"("ID") ON DELETE SET NULL ON UPDATE CASCADE
ALTER TABLE "Table1" ALTER COLUMN "ID" RESTART WITH 15
ALTER TABLE "Table2" ALTER COLUMN "ID" RESTART WITH 2
ALTER TABLE "Table3" ALTER COLUMN "ID" RESTART WITH 4
CREATE USER SA PASSWORD ""
GRANT DBA TO SA
SET WRITE_DELAY 0 MILLIS
SET SCHEMA PUBLIC
INSERT INTO "Table1" VALUES(0,0,'123',0)
INSERT INTO "Table1" VALUES(1,1,'456',NULL)
INSERT INTO "Table1" VALUES(2,2,'789',0)
INSERT INTO "Table1" VALUES(3,0,'012',1)
INSERT INTO "Table1" VALUES(6,3,'345',NULL)
INSERT INTO "Table1" VALUES(7,1,'678',1)
INSERT INTO "Table1" VALUES(8,0,'123',NULL)
INSERT INTO "Table1" VALUES(9,0,'123',1)
INSERT INTO "Table1" VALUES(10,1,'456',0)
INSERT INTO "Table1" VALUES(11,1,'456',1)
INSERT INTO "Table1" VALUES(12,1,'456',0)
INSERT INTO "Table1" VALUES(13,1,'123',NULL)
INSERT INTO "Table1" VALUES(14,1,'123',0)
INSERT INTO "Table2" VALUES(0,'B')
INSERT INTO "Table2" VALUES(1,'E')
INSERT INTO "Table3" VALUES(0,'A')
INSERT INTO "Table3" VALUES(1,'C')
INSERT INTO "Table3" VALUES(2,'D')
INSERT INTO "Table3" VALUES(3,'F')

似乎没有导出查询,这里是他们的结果

查询1

加入主表:

SELECT "Table1"."ID", "Table3"."content" AS "Table3_content", "Table1"."model_number", "Table2"."content" AS "Table2_content"
    FROM "Table1"
        LEFT OUTER JOIN "Table2" ON "Table1"."ID_to_part2" = "Table2"."ID"
        LEFT OUTER JOIN "Table3" ON "Table1"."ID_to_part1" = "Table3"."ID"
    ORDER BY "ID" ASC

导致:

ID    Table3_content    model_number    Table2_content
0     A                 123             B
1     C                 456             
2     D                 789             B
3     A                 012             E
6     F                 345             
7     C                 678             E
8     A                 123             
9     A                 123             E
10    C                 456             B
11    C                 456             E
12    C                 456             B
13    C                 123             
14    C                 123             B

查询2

如果第3部分也匹配,则唯一索引的第一部分可以“破坏”所需唯一索引的行/元组。换句话说,其他行不构成威胁(Query1 减去 Query2)。

SELECT *
    FROM "Table1"
    -- It seem HSQLDB won't support tuples as in WHERE (col1, col2) IN ( SELECT col1, col2 FROM
    WHERE "ID_to_part1" IN (
            SELECT "ID_to_part1"
                FROM "Table1"
                GROUP BY "ID_to_part1", "model_number"
                HAVING COUNT(*) > 1
        ) AND "model_number" IN (
            SELECT "model_number"
                FROM "Table1"
                GROUP BY "ID_to_part1", "model_number"
                HAVING COUNT(*) > 1
        )
    ORDER BY "ID_to_part1" ASC, "model_number" ASC, "ID_to_part2" ASC, "ID" ASC

导致:

ID    ID_to_part1    model_number    ID_to_part2
8     0              123             
0     0              123             0
9     0              123             1
13    1              123             
14    1              123             0
1     1              456             
10    1              456             0
12    1              456             0
11    1              456             1

查询3

将“破坏”所需唯一索引的行/元组。

SELECT "Table1".*
    FROM "Table1"
        JOIN (
            SELECT "ID_to_part1", "model_number", "ID_to_part2"
                FROM "Table1"
                GROUP BY "ID_to_part1", "model_number", "ID_to_part2"
                HAVING COUNT(*) > 1
        ) AS "non_unique_model"
            ON "Table1"."ID_to_part1"="non_unique_model"."ID_to_part1"
                AND "Table1"."model_number"="non_unique_model"."model_number"
                AND "Table1"."ID_to_part2"="non_unique_model"."ID_to_part2"
    ORDER BY "ID_to_part1" ASC, "model_number" ASC, "ID_to_part2" ASC, "ID" ASC

导致:

ID    ID_to_part1    model_number    ID_to_part2
10    1              456             0
12    1              456             0

美化重要表架构

CREATE CACHED TABLE "Table1"(
    "ID" INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 0) NOT NULL PRIMARY KEY,
    "ID_to_part1" INTEGER NOT NULL,
    "model_number" VARCHAR_IGNORECASE(3) NOT NULL,
    "ID_to_part2" INTEGER
)
CREATE CACHED TABLE "Table2"(
    "ID" INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 0) NOT NULL PRIMARY KEY,
    "content" VARCHAR_IGNORECASE(1) NOT NULL
)
4

3 回答 3

1

欢迎来到 SO!我觉得你的问题有点难以阅读。

编辑:

CREATE TABLE table1 (
    id INTEGER NOT NULL PRIMARY KEY,
    data1 INTEGER NOT NULL
);

CREATE TABLE table2 (
    id INTEGER NOT NULL PRIMARY KEY REFERENCES table1(id),
    data2 INTEGER NOT NULL
);

表1中有记录。对于表 1 中的每条记录,表 2 中都有 0 个或一个对应的记录。

这种模式类似于表继承。

进一步说明:

这将允许您必须跟踪数据。

id   data1         id  data2
----------         ---------
 0    1234          0     42
 1    5678          2     57
 2    9012

看到table1中id为0和2的记录在table2中有对应的记录。id 为 1 的记录没有。

附言

请注意,您也可以将事物组合到一张表中。这是否可取取决于您的情况。

CREATE TABLE table1 (
    id INTEGER NOT NULL PRIMARY KEY,
    data1 INTEGER NOT NULL,
    data2 INTEGER NULL
);
于 2013-10-26T21:41:41.563 回答
0

你的问题是:

我不想在第一个表的重要部分上重复,所以我想创建一个唯一键,但似乎无法创建一个键,其中包含一个可能包含 null 的列。

您不希望 Table1 中的“重要部分”出现重复,但不清楚哪些部分必须是唯一的。假设“重要部分”是这三列中的一些:

"ID_to_part1" INTEGER,"model_number" VARCHAR_IGNORECASE(3) NOT NULL,"ID_to_part2" INTEGER

A)如果您在“model_number”上创建一个唯一约束,根据定义它是 NOT NULL:

CONSTRAINT UNIQUE ("model_number")

那么 model_number 值是唯一的,但是两个不同的模型可以有相同的 ID_to_part1

B)除了(A)你可以有这个约束:

CONSTRAINT UNIQUE ("model_number", "ID_to_part1")

然后每个model_number 将对应一个唯一的ID_to_part1。如果 ID_to_part1 上没有 NOT NULL,那么对于那些没有额外部分的 model_number 值,它可以包含 NULL。

C)除了(A)之外,您还可以拥有:

CONSTRAINT UNIQUE ("model_number", "ID_to_part2")

这与 (B) 具有相同的效果,但对于 ID_to_part2 列。

您的 SELECT 语句是正确的。它显示所有模型以及它们可能拥有的任何可选信息。

简而言之,您可以对可以包含 NULL 的列进行 UNIQUE 约束。但也需要对 model_number 的 UNIQUE 约束。

编辑:

OP再次编辑了问题,要求“model_number”不是唯一的,只有三列一起是唯一的,其中一些可以存储NULL并且NULL不能重复。这是 HSQLDB 1.8 无法实现的。在 HSQLDB 2.x 中有一个设置 SET DATABASE SQL UNIQUE NULLS 可以更改为 FALSE 以允许这样做。在这种情况下,三列上只需要一个 UNIQUE 约束。

于 2013-10-29T17:12:43.100 回答
0

我想创建一个唯一键,但似乎无法创建包含可能包含 null 的列的键。

我的理解是,您有一个 FK,要在其上构建索引以提高性能,并且 FK 可能包含空值(如@Paul Draper 的解决方案中所示)。

我不是 HSQLDB 专家,但用户指南在 Constraints 部分下说:“自 1.7.2 版以来,UNIQUE 约束和索引相对于 NULL 值的行为已更改为符合 SQL 标准。一行,其中任何 UNIQUE 约束列的值为 NULL,始终可以添加到表中。因此,如果其中一个值为 NULL,则多行可以包含 UNIQUE 列的相同值。 "

我理解这意味着您可以在数据库版本 1.7.2 中的 FK 上构建索引,即使 FK 值为 Null 的列 conain 行也是如此。

于 2013-10-26T21:58:15.210 回答