【发布时间】:2016-08-17 09:58:56
【问题描述】:
更新
在我使用 postgresql 的 rails 应用程序中,我编写了一个 Active Record 查询来查找一组志愿者记录,然后按first_name、last_name、email 对它们进行排序。此外,first_name 和 last_name 可能是 null(两者都将是 null 或两者都不是 null)。 Active Record 生成的 sql 如下所示:
SELECT DISTINCT LOWER(COALESCE("volunteers"."first_name", "volunteers"."email")), LOWER(COALESCE("volunteers"."last_name", "volunteers"."email")), LOWER("volunteers"."email"), "volunteers".*
FROM "volunteers"
INNER JOIN "volunteer_list_connectors" ON "volunteer_list_connectors"."volunteer_id" = "volunteers"."id"
INNER JOIN "volunteer_lists" ON "volunteer_lists"."id" = "volunteer_list_connectors"."volunteer_list_id"
WHERE ((volunteer_lists.organizer_id = 1 AND organizer_type = 'Organization') OR (volunteer_lists.organizer_id IN (1) AND organizer_type = 'Collaborative'))
ORDER BY
LOWER(COALESCE("volunteers"."first_name", "volunteers"."email")) ASC,
LOWER(COALESCE("volunteers"."last_name", "volunteers"."email")) ASC,
LOWER("volunteers"."email") ASC
使用 Postgres 的 psql 工具,我将该 sql 复制并粘贴到我的开发机器和服务器上的 Postgres 中。在我的开发机器 (Mac) 上,该 sql 对以下列表进行了排序:
- 志愿者 [first_name:'Alex',last_name:'Diego',电子邮件: 'a.diego@person.com']
- 志愿者 [名字:
null,姓氏:null,电子邮件: 'cxxr@person.com'] - 志愿者 [first_name:'Josh',last_name:'Broger',电子邮件: 'broger@person.com']
- 志愿者 [first_name:'Josh',last_name:'Broger',电子邮件: 'jcool@person.com']
- 志愿者 [名字:'Josh',姓氏:'Kenton',电子邮件: 'aj@person.com']
在我的生产环境 (Ubuntu) 中,Postgres 通过对列表进行排序来响应相同的代码:
- 志愿者 [名字:
null,姓氏:null,电子邮件: 'cxxr@person.com'] - 志愿者 [first_name:'Alex',last_name:'Diego',电子邮件: 'a.diego@person.com']
- 志愿者 [first_name:'Josh',last_name:'Broger',电子邮件: 'broger@person.com']
- 志愿者 [first_name:'Josh',last_name:'Broger',电子邮件: 'jcool@person.com']
- 志愿者 [名字:'Josh',姓氏:'Kenton',电子邮件: 'aj@person.com']
区别在于只有一个电子邮件地址的志愿者总是排在其他志愿者之前。生产环境和开发环境都运行 PostgreSQL 9.5.2。两者都运行 Rails 4.2.6。
我一直无法弄清楚为什么我的应用程序在开发和生产中表现不同。
有人有什么想法吗?非常感谢任何需要进一步探索的帮助/领域!
更新 2
根据评论中的要求,这是我的开发机器和生产机器的 psql 输出。我稍微更改了查询以使图片可读。在这个查询中,我添加了一个LIMIT 10,我只要求first_name、last_name 和email,而不是整个记录。你必须相信我的话,这两个数据库都包含只有email 地址的volunteers 记录,以及first_name、last_name 和email 的记录。如果我的生产数据库按预期工作,它的输出将类似于第一张图片中的输出。
这个屏幕抓取显示了我的开发机器的psql 输出(好):
这个屏幕抓取显示了我的生产机器的psql 输出(坏)。因为这是输出中的真实用户数据,所以我模糊了电子邮件地址。但重要的一点是带有first_name = null 和last_name = null 的记录总是排在第一位,即使电子邮件地址的第一个字母是“Z”:
我可能是错的,但我认为生产机器的输出应该在每一列中显示电子邮件地址,COALESCE() 是否正常运行
回答
感谢@ClémentPrévost(以及@muistooshort 向我提出了正确的问题!)我能够找到答案:我认为没有正确排序的生产记录实际上在first_name 和@ 中有空白字符串987654354@ 而不是 null 值(并且 COALESCE() 只过滤掉 null 值,而不是空白字符串)。我用NULLIF("volunteers"."first_name", \'\') 更新了我的代码,一切都开始正确排序了!
SELECT DISTINCT LOWER(COALESCE(NULLIF("volunteers"."first_name", ''), "volunteers"."email")), LOWER(COALESCE(NULLIF("volunteers"."last_name", ''), "volunteers"."email")), LOWER("volunteers"."email"), "volunteers".*
FROM "volunteers"
INNER JOIN "volunteer_list_connectors" ON "volunteer_list_connectors"."volunteer_id" = "volunteers"."id"
INNER JOIN "volunteer_lists" ON "volunteer_lists"."id" = "volunteer_list_connectors"."volunteer_list_id"
WHERE ((volunteer_lists.organizer_id = 1 AND organizer_type = 'Organization') OR (volunteer_lists.organizer_id IN (1) AND organizer_type = 'Collaborative'))
ORDER BY
LOWER(COALESCE(NULLIF("volunteers"."first_name", ''), "volunteers"."email")) ASC,
LOWER(COALESCE(NULLIF("volunteers"."last_name", ''), "volunteers"."email")) ASC,
LOWER("volunteers"."email") ASC
【问题讨论】:
-
所以你的 ORDER BY 是
LOWER(COALESCE("volunteers"."first_name", "volunteers"."email")) ASC, LOWER(COALESCE("volunteers"."last_name", "volunteers"."email")) ASC, LOWER("volunteers"."email") ASC,对吧? -
您的开发和生产数据库服务器是否使用相同的排序规则/语言环境?
-
PostgreSQL 将使用来自底层操作系统的一些整理信息,但不应将
'c'排序在'a'之前。你确定没有空格或 Unicode 奇怪吗?生产中没有自定义排序规则?您能否向我们展示您在生产中查询的原始psql输出? -
这些真的是空字符串还是空字符串?您可以使用
\pset null ¤来区分两者。 -
到目前为止,我和@ClémentPrévost 在一起。 COALESCE 被破坏的可能性很小,
first_name更可能不是您认为的那样。
标签: postgresql sorting