如何使用可选的FK制作索引?

时间:2013-10-26 21:30:09

标签: sql relational-database hsqldb

原始问题

因此,我没有创建的业务模型具有可选关系(如在ER模型中)。我工作过DB已经有一段时间了,所以我可能会遗忘一些东西。目前第一个表的FK(外键)指向第二个表的PK(主键),这是一个ID;我不记得这个术语,但它是“假的”,而不是RDBMS(关系数据库管理系统)使用的“真实”。为简单起见,我们假设只有2个表。

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

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

第二个是制作一个“链接”表(他/她的名词),这是一个多对多的关系,但我认为那些只是那些有超过1种可能关系的条目;有0-1关系的人不会使用它。无论如何,我仍然面临同样的问题,在那里不需要在“链接”表中有一个条目。 IIRC,此类表格的2个“边”必须包含有效的候选密钥。

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

谢谢你的帮助

fredt请求

现在包含 3 rd 示例

followind子节包含使用命令SCRIPT 'PATH\TO\NAME.sql'从Apache OpenOffice Base导出的半SQL。原始文件及其导出及其未导出的查询位于How to make an index with an optional FK? example 3

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

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

QUERY2

如果3 rd 也匹配,那么唯一索引的第一部分可以“破坏”所需唯一索引的行/元组。换句话说,其他行不是威胁(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

QUERY3

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

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
)

3 个答案:

答案 0 :(得分: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
);

table1中有记录。对于table1中的每条记录,table2中有零个或一个相应的记录。

此模式类似于表继承。

进一步解释:

这将允许您必须关注数据。

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

看到table1中带有id 0和2的记录在table2中有相应的记录。 id为1的记录不会。

P.S。

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

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

答案 1 :(得分:0)

  

我想创建一个唯一的密钥,但似乎无法创建密钥   其中包括可能包含null的列。

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

我不是HSQLDB的专家,但是Constraints部分下的user guide说: “从版本1.7.2开始,UNIQUE约束和索引相对于NULL值的行为已经改变为符合SQL标准。可以始终添加一行,其中任何UNIQUE约束列的值为NULL如果其中一个值为NULL,那么多行可以包含UNIQUE列的相同值。

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

答案 2 :(得分:0)

你的问题是:

  

我不想要   重复第一张表的重要部分,所以我想   创建一个唯一的密钥但似乎无法创建哪个密钥   包括一个可能包含null的列。

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

"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约束。