排序有多对多的关系

时间:2015-05-16 01:00:49

标签: sql sqlite postgresql sorting

我有3个表personperson_speaks_languagelanguage

  • person有80条记录
  • 语言有2条记录

我有以下记录

  • 前10个人说一种语言
  • 前70人(包括第一组)说2种语言
  • 最后10个人不会说任何语言

以下示例我希望按语言对人员进行排序,如何正确地进行排序。

我试图使用以下SQL但看起来很奇怪

SELECT "person".*
FROM "person"
  LEFT JOIN "person_speaks_language" ON "person"."id" = "person_speaks_language"."person_id"
  LEFT JOIN "language" ON "person_speaks_language"."language_id" = "language"."id"
ORDER BY "language"."name"
  ASC

数据集

71,Catherine,Porter,male,NULL
72,Isabelle,Sharp,male,NULL
73,Scott,Chandler,male,NULL
74,Jean,Graham,male,NULL
75,Marc,Kennedy,male,NULL
76,Marion,Weaver,male,NULL
77,Melvin,Fitzgerald,male,NULL
78,Catherine,Guerrero,male,NULL
79,Linnie,Strickland,male,NULL
80,Ann,Henderson,male,NULL
11,Daniel,Boyd,female,English
12,Ora,Beck,female,English
13,Hulda,Lloyd,female,English
14,Jessie,McBride,female,English
15,Marguerite,Andrews,female,English
16,Maurice,Hamilton,female,English
17,Cecilia,Rhodes,female,English
18,Owen,Powers,female,English
19,Ivan,Butler,female,English
20,Rose,Bishop,female,English
21,Franklin,Mann,female,English
22,Martha,Hogan,female,English
23,Francis,Oliver,female,English
24,Catherine,Carlson,female,English
25,Rose,Sanchez,female,English
26,Danny,Bryant,female,English
27,Jim,Christensen,female,English
28,Eric,Banks,female,English
29,Tony,Dennis,female,English
30,Roy,Hoffman,female,English
31,Edgar,Hunter,female,English
32,Matilda,Gordon,female,English
33,Randall,Cruz,female,English
34,Allen,Brewer,female,English
35,Iva,Pittman,female,English
36,Garrett,Holland,female,English
37,Johnny,Russell,female,English
38,Nina,Richards,female,English
39,Mary,Ballard,female,English
40,Adrian,Sparks,female,English
41,Evelyn,Santos,female,English
42,Bess,Jackson,female,English
43,Nicholas,Love,female,English
44,Fred,Perkins,female,English
45,Cynthia,Dunn,female,English
46,Alan,Lamb,female,English
47,Ricardo,Sims,female,English
48,Rosie,Rogers,female,English
49,Susan,Sutton,female,English
50,Mary,Boone,female,English
51,Francis,Marshall,male,English
52,Carl,Olson,male,English
53,Mario,Becker,male,English
54,May,Hunt,male,English
55,Sophie,Neal,male,English
56,Frederick,Houston,male,English
57,Edwin,Allison,male,English
58,Florence,Wheeler,male,English
59,Julia,Rogers,male,English
60,Janie,Morgan,male,English
61,Louis,Hubbard,male,English
62,Lida,Wolfe,male,English
63,Alfred,Summers,male,English
64,Lina,Shaw,male,English
65,Landon,Carroll,male,English
66,Lilly,Harper,male,English
67,Lela,Gordon,male,English
68,Nina,Perry,male,English
69,Dean,Perez,male,English
70,Bertie,Hill,male,English
1,Nelle,Gill,female,Spanish
2,Lula,Wright,female,Spanish
3,Anthony,Jensen,female,Spanish
4,Rodney,Alvarez,female,Spanish
5,Scott,Holmes,female,Spanish
6,Daisy,Aguilar,female,Spanish
7,Elijah,Olson,female,Spanish
8,Alma,Henderson,female,Spanish
9,Willie,Barrett,female,Spanish
10,Ada,Huff,female,Spanish
11,Daniel,Boyd,female,Spanish
12,Ora,Beck,female,Spanish
13,Hulda,Lloyd,female,Spanish
14,Jessie,McBride,female,Spanish
15,Marguerite,Andrews,female,Spanish
16,Maurice,Hamilton,female,Spanish
17,Cecilia,Rhodes,female,Spanish
18,Owen,Powers,female,Spanish
19,Ivan,Butler,female,Spanish
20,Rose,Bishop,female,Spanish
21,Franklin,Mann,female,Spanish
22,Martha,Hogan,female,Spanish
23,Francis,Oliver,female,Spanish
24,Catherine,Carlson,female,Spanish
25,Rose,Sanchez,female,Spanish
26,Danny,Bryant,female,Spanish
27,Jim,Christensen,female,Spanish
28,Eric,Banks,female,Spanish
29,Tony,Dennis,female,Spanish
30,Roy,Hoffman,female,Spanish
31,Edgar,Hunter,female,Spanish
32,Matilda,Gordon,female,Spanish
33,Randall,Cruz,female,Spanish
34,Allen,Brewer,female,Spanish
35,Iva,Pittman,female,Spanish
36,Garrett,Holland,female,Spanish
37,Johnny,Russell,female,Spanish
38,Nina,Richards,female,Spanish
39,Mary,Ballard,female,Spanish
40,Adrian,Sparks,female,Spanish
41,Evelyn,Santos,female,Spanish
42,Bess,Jackson,female,Spanish
43,Nicholas,Love,female,Spanish
44,Fred,Perkins,female,Spanish
45,Cynthia,Dunn,female,Spanish
46,Alan,Lamb,female,Spanish
47,Ricardo,Sims,female,Spanish
48,Rosie,Rogers,female,Spanish
49,Susan,Sutton,female,Spanish
50,Mary,Boone,female,Spanish
51,Francis,Marshall,male,Spanish
52,Carl,Olson,male,Spanish
53,Mario,Becker,male,Spanish
54,May,Hunt,male,Spanish
55,Sophie,Neal,male,Spanish
56,Frederick,Houston,male,Spanish
57,Edwin,Allison,male,Spanish
58,Florence,Wheeler,male,Spanish
59,Julia,Rogers,male,Spanish
60,Janie,Morgan,male,Spanish
61,Louis,Hubbard,male,Spanish
62,Lida,Wolfe,male,Spanish
63,Alfred,Summers,male,Spanish
64,Lina,Shaw,male,Spanish
65,Landon,Carroll,male,Spanish
66,Lilly,Harper,male,Spanish
67,Lela,Gordon,male,Spanish
68,Nina,Perry,male,Spanish
69,Dean,Perez,male,Spanish
70,Bertie,Hill,male,Spanish

更新

期望结果是:每个人必须只使用语言顺序出现一次

为了进一步解释这个案例,我将使用一个新的小数据集,只使用人员ID和语言名称

1,English
2,English
3,English
4,English
19,English
1,Spanish
2,Spanish
3,Spanish
4,Spanish
5,Spanish
14,Spanish
15,Spanish
16,Spanish
19,Spanish
21,Spanish
25,Spanish

我使用相同的订单,但如果我使用限制,例如LIMIT 8,结果将是

1,English
2,English
3,English
4,English
19,English
1,Spanish
2,Spanish
3,Spanish

预期结果是

1,English
2,English
3,English
4,English
19,English
5,Spanish
14,Spanish
15,Spanish

我想做什么

我尝试做的是对可能与Y建立多对多关系的X列表进行排序,分页和过滤,在这种情况下,X是人,Y是语言。我需要以一般的方式做到这一点。如果我想通过某些Y属性排序列表,我发现了一个麻烦。

列表将以这种方式显示:

firstname, lastname, gender  , languages
Daniel   , Boyd    , female  , English Spanish
Ora      , Beck    , female  , English
Anthony  , Jensen  , female  , Spanish
....

我只需要返回一个ID正确的数组

这是我需要的主要原因,结果只出现在一个人身上,因为ORM(我正在使用)尝试水合每个结果,如果我使用偏移和限制对结果进行分页。结果可能不是预期的。我做了许多关系的假设

我无法使用string_agggroup_concat,因为我不知道真实数据,我不知道是整数还是字符串

2 个答案:

答案 0 :(得分:2)

如果您希望每个人只出现一次,那么您需要由该人聚合。如果你想要语言列表,你需要以某种方式组合它们,脑海就会想到连接。

使用双引号表示Postgres或Oracle给我。这是Postgres的语法:

SELECT p.id, string_agg(l.name) as languages
FROM person p LEFT JOIN 
     person_speaks_language psl
     ON p.id = psl.person_id LEFT JOIN
     language l
     ON psl.language_id = l.id
GROUP BY p.id
ORDER BY COUNT(l.name) DESC, languages;

大多数数据库都存在与string_agg()类似的功能。

答案 1 :(得分:1)

Bertie Hill出现在两行中,每行一种语言,即关系模型数据表格视图,没有任何问题。不依赖于数据值或数据值的数量。这完全正确且没有混淆。

但是在这里,要求很混乱,因为你真的想要三个单独的列表:

  • 说一种语言
  • 说两种语言[或当前语言文件中的语言数量]
  • 不说[存档]语言)...

但是你想要将这三个列表放在一个列表中。

连接数据值永远不是一个好主意。这违反了基本标准,特别是1NF。这可能很常见,但这是一个严重错误。它可能由所谓的理论家和#34;教授,但它仍然是一个严重错误。即使在结果集中,也是如此。

  1. 这会产生混淆,例如我在顶部详细说明。

  2. 对于串联字符串,随着语言数量的变化,该连接字段的宽度将增大,并最终超出空间,无论它出现在何处(例如,屏幕上字段的宽度)。

  3. 为什么它不正确,不可扩展,不合标准的原因中的两个。

    顺便说一下,在你的"数据集" (它不是你的代码产生的结果集),性别似乎很好地混淆了。

    因此答案,也是唯一正确的答案,即使它不受欢迎,也就是说你的代码是正确的(它可以清理,确定),你必须教育用户重新认识不合标准的代码或报告。

    • 您可以按person.name排序(而不是language.name),然后编写更智能的SQL,以便(例如)person.name不会在第二行和后续行上重复说不止一种语言,等等。这只是非常印刷。

    对于那些坚持不合标准代码的人来说,非答案是戈登的回应。

    对评论的回应

    在关系模型中:

    • 行没有命令,这被视为物理或实施方面,我们无法控制,无论如何都会发生变化,我们被警告不要依赖。如果在输出结果集中找到订单,那么我们必须ORDER BY,这就是它的目的。

    • 数据具有意义,并且该含义在关系密钥中进行。意义不能在代理人(即ID列)中携带。

    将自己限制在您提供的文件(它们不是表格)中,数据中没有这样的东西:

      
        
    • 前10个说一种语言的人
    •   

    获得说一种语言的人很简单,我相信你已经明白:

      SELECT  person.first_name,
              person.last_name
          FROM person P,
          (SELECT person_id
              FROM person_speaks_language
              GROUP BY person_id
              HAVING COUNT(*) = 1          -- change this for 2 languages, etc
              ) AS PL
          WHERE P.person_id = PL.person_id
    

    但是"首先" ? "第一"按什么标准?记录创建日期?

          ORDER BY date_created            -- if it exists in the data
    

    记录ID不会提供任何内容:随着记录的添加和删除,任何"顺序"最初可能存在的东西完全丢失了。

    根据定义,你无法从某些东西中提取意义或赋予意义,而这些意义没有任何意义。如果记录ID是相关的,即。您将出于某种目的使用它,然后它不是记录ID,将字段命名为实际的字段。

    我没有看到,我不明白,"数据集"之间差异的相关性。和更新的"小数据集"。 "数据集"大小是无关紧要的,字段标题是无关紧要的,结果集意味着什么,是相关的。

    问题不在于某些限制"在关系模型中,问题是(a)数据的固定视图,以及(b)您对关系模型的内容缺乏了解 is,它做了什么,了解哪个使得整个问题消失了,我们留下了一个简单的SQL(标记为)"如何"题。例如。如果我有一个人员和语言的关系数据库,没有ID列,那么我就无法从数据中获取任何我无法从中生成的报告。

    请尝试使用在数据中传达含义的示例,以及您要做的事情。

      

    预期结果是:每个人必须只出现一次

    它们只出现一次(针对每种语言)

      

    使用语言顺序

    好吧,language文件中没有订单。根据数据,我们可以在结果集中为您提供一些订单,无论订单是否有意义。例如。 language.name.当然,很多人会说每种语言,所以你想在language.name中订购什么顺序?怎么样last_name, first_name.记录ID对用户没有意义,所以我不会在结果集中显示它们。 NULL也没有意义,含糊不清,所以我会在这里明确表达含义。这就是你所拥有的,整理了一下:

        SELECT  [language] = CASE name
                    WHEN NULL THEN "[None]"
                    ELSE name
                    END, 
                last_name, 
                first_name
            FROM person P
                LEFT JOIN person_speaks_language PL
                    ON P.id = PL.person_id
                LEFT JOIN language L
                    ON PL.language_id = L.id
            ORDER BY name, 
                     last_name, 
                     first_name
    

    但是你有:

      

    预期结果是

    示例数据与您的文字说明相矛盾:

      

    预期结果是:每个人必须只使用语言顺序出现一次

    现在,如果我忽略了文本,并根据需要检查示例数据

    • (这是一件可怕的事情,因为我正在加入你关注数据的错误活动,而不是理解其含义),

    您似乎希望此人只出现一次,完全停止,无论他们说多少种语言。您的示例数据毫无意义,因此无法要求我重现它。看看这是否有一些意义。

        SELECT  last_name,
                first_name,
                [language] = (                   -- correlated subquery
            SELECT TOP 1                         -- get the "first" language
                CASE name                        -- make meaning of null explicit
                    WHEN NULL THEN "[None]"
                    ELSE name
                    END
                FROM person_speaks_language PL
                    JOIN language L
                        ON PL.language_id = L.id
                WHERE P.id = PL.person_id        -- the subject person
                ORDER BY name                    -- id would be meaningless
                )
            FROM person P                        -- vector for person, once
            ORDER BY last_name,
                     first_name
    

    现在,如果您只想说一种语言的人(存档):

        SELECT  last_name,
                first_name,
                [language] = (                     -- correlated subquery
            SELECT TOP 1                           -- get the "first" language
                    name
                FROM person_speaks_language PL
                    JOIN language L
                        ON PL.language_id = L.id
                WHERE P.id = PL.person_id          -- the subject person
                ORDER BY name                      -- id would be meaningless
                )
            FROM person P,
                (
                SELECT  DISTINCT person_id         -- just one occ, thanks
                    FROM person_speaks_language PL -- vector for speakers
                    ) AS PL_1
            WHERE P.id = PL_1.person_id            -- join them to person fields
    

    在任何一个解决方案中,都没有任何外部联接可供查看。 LEFTRIGHT会让您感到困惑。不要试图"得到所有东西",这样你就可以"看到"数据,然后对结果集进行修改,破解和砍掉,以便从中得到你想要的东西。不,忘记记录备案系统中的数据只获取您想要的内容

    对更新的响应

      

    我试图用数据集来解释这个案例,我想我做的事情比他们实际上更难了

    是的,你做到了。然后查看更新......

    1. 简短的回答是,摆脱ORM。它没有任何价值:

      • 您可以从直接填充对象的查询中访问RDB。我们在气喘吁吁的野兽出现之前已经做了几十年。特别是如果您理解并实施Open Architecture Standards

      • 此外,如证据所示,它会产生大量问题。在这里,您正试图解决ORM的疯狂限制。

      • 如果您的数据已标准化,则关联是一个直截了当的问题。

    2. 答案很长......请阅读this Answer。我相信您会明白,您设计应用程序组件的方法,您的窗口设计,将会改变。您的所有查询都将被简化,您只能获得特定窗口或对象所需的查询。

      问题可能完全消失(除了可能的分页,你可能需要一种方法)。

    3. 然后请仔细考虑这些架构问题,并对问题作出具体评论。