Sql 使用多对多关系进行排序

Sql 使用多对多关系进行排序,sql,sqlite,postgresql,sorting,Sql,Sqlite,Postgresql,Sorting,我有三个表人,人讲的是语言和语言 此人有80项记录 语言有2条记录 我有以下记录 前10个人讲一种语言 前70人(包括第一组)讲两种语言 最后10个人不会说任何语言 下面是一个例子,我想按语言对人进行排序,如何正确地进行排序 我正在尝试使用下面的SQL,但是看起来很奇怪 SELECT "person".* FROM "person" LEFT JOIN "person_speaks_language" ON "person"."id" = "person_speaks_languag

我有三个表
人讲的是语言
语言

  • 此人有80项记录
  • 语言有2条记录
我有以下记录

  • 前10个人讲一种语言
  • 前70人(包括第一组)讲两种语言
  • 最后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
更新 预期结果是:每个人只能使用语言顺序出现一次

为了进一步解释这个案例,我将使用一个新的小数据集,只使用person 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
我使用相同的顺序,但如果我使用限制,例如限制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
我想做什么 我要做的是对X列表进行排序、分页和过滤,这些X列表可能与Y有多对多的关系,在这种情况下,X是个人,Y是语言。我需要用一般的方法来做。如果我想按Y属性对列表排序,我发现了一个问题

列表将以这种方式显示:

firstname, lastname, gender  , languages
Daniel   , Boyd    , female  , English Spanish
Ora      , Beck    , female  , English
Anthony  , Jensen  , female  , Spanish
....
我只需要返回一个ID顺序正确的数组

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


我不能使用
string\u agg
group\u concat
,因为我不知道实际数据,我不知道是整数还是字符串如果你想让每个人只出现一次,那么你需要由那个人进行聚合。如果您想要语言列表,您需要以某种方式将它们组合在一起,这时会想到连接

使用双引号对我来说意味着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\u agg()
的功能。

Bertie Hill出现在两行中,每行使用一种语言,也就是每个关系模型的数据表视图。不依赖于数据值或数据值的数量。这是完全正确的,没有混淆

但在这里,需求是混乱的,因为您确实需要三个单独的列表:

  • 说一种语言
  • 讲两种语言[或语言文件中当前的语言数]
  • 不会说任何语言[存档]
但是你想把这三个列表放在一个列表中

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

  • 这会造成混乱,正如我在顶部详述的那样

  • 对于串联字符串,随着语言数量的变化,该串联字段的宽度将增加,并最终超过空间,无论它出现在哪里(例如屏幕上的字段宽度)

  • 这只是它不正确、不可扩展、不符合标准的众多原因中的两个

    顺便说一句,在“数据集”(它不是代码生成的结果集)中,两性似乎很好地混合在一起

    因此,答案是正确的,也是唯一正确的答案,即使它不受欢迎,也是正确的,那就是你的代码是正确的(当然,它可以被清除掉),你必须教育用户重新认识不符合标准的代码或报告的危险

    • 您可以按
      person.name
      (而不是按
      language.name
      )排序,然后编写更智能的SQL,以便(例如)对于说多种语言的人来说,
      person.name
      不会在第二行和后续行重复,等等。这简直是漂亮的打印
    对于那些坚持有一天会被打破的低于标准的代码的人来说,答案是戈登的回应

    对评论的答复 在关系模型中:

    • 行没有顺序,这被认为是一个物理或实现方面,我们无法控制它,它无论如何都会发生变化,我们被警告不要依赖它。如果在输出结果集中寻找顺序,那么我们必须告诉我们,
      orderby,
      这就是它在生活中的目的

    • 数据是有意义的,而这种意义是通过关系键传递的。不能在代理项(即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)您对关系模型是什么及其作用缺乏了解
        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