MySQL order by - 排序不正确

Posted

技术标签:

【中文标题】MySQL order by - 排序不正确【英文标题】:MySQL order by - does not sort properly 【发布时间】:2022-01-24 03:23:54 【问题描述】:

这看起来很简单...我正在运行 SQL 查询以输出到 CSV。我想按查询中的第一列排序,然后是第二列。对于使用 CSV 作为输入的程序来说,这一点至关重要,因为它正在寻找这些值的变化以触发写入,并从这两个字段中具有相同值的所有行中累积数据。我怀疑这与我正在输出到 CSV 的事实有关,如下所述。

我开始:

SELECT claim_no, form_no, ...
ORDER BY claim_no, form_no

claim_no 和 form_no 都是 VARCHAR 字段,但只包含数字。输出文件没有排序,除了巧合(可能按照插入的大致顺序)。

我考虑了 form_no 可能为 NULL 的可能性,这可能会影响行为,因为表是通过 LEFT OUTER JOIN 连接的。所以我把它改成了:

SELECT claim_no, form_no, ...
ORDER BY claim_no, IFNULL(form_no, '0')

仍然没有运气。我在 SO 中寻找任何想法,似​​乎 CASTing to INT 会有所帮助,所以我有:

SELECT claim_no, form_no, ...
ORDER BY CAST(claim_no AS UNSIGNED), CAST(IFNULL(form_no, '0') AS UNSIGNED)

不。以下是 CSV 文件中的示例:

...
"5799", "9823638", ...
"5800", "9824370", ...
"5800", "9824056", ...
...
"4258", "9728616", ...
"4258", "9728782", ...
...
"4258", "9719766", ...
...

但是如果我在 mysql 命令行中运行这个查询:

select claim_no, form_no from claim left outer join transportation_claim on claim_id = claim.id where claim_no = '5800' order by claim_no, form_no;

...我得到了索赔 #5800 的正确排序结果。

除非有人知道为什么会发生这种情况,否则我将不得不更改使用 CSV 文件作为输入的程序 - 这是我不愿做的事情。

为了完整起见,这里是整个查询。 CSV 文件有 340k 行,请原谅我没有包含它。

(SELECT
    'claim_no', 'document_no', 'date_received', 'toll_type', 'toll_amount', 'driver_amount',
    'downtime_amount', 'vehicle_amount', 'claimed_amount', 'distance', 'generator_reg_no',
    'transporter_reg_no', 'city_name', 'distance_rate',
    'plt_count', 'plt_weight', 'mt_count', 'mt_weight', 'ag_count', 'ag_weight', 'ls_count', 'ls_weight')
UNION
(SELECT 
    claim_no,
    IF(form_no IS NULL, CONCAT('0000', claim_no), form_no),
    IF(transportation_claim.date_received IS NULL, 'No Date Received', transportation_claim.date_received),
    IF(toll_type IS NULL, 'Barge', toll_type),
    IF(toll_amount IS NULL, 0, toll_amount), 
    IF(driver_amount IS NULL, 0, driver_amount),
    IF(downtime_amount IS NULL, 0, downtime_amount),
    IF(vehicle_amount IS NULL, 0, vehicle_amount),
    IF(claimed_amount IS NULL, 0, IF(toll_amount IS NULL, claimed_amount, claimed_amount - toll_amount)),
    IF(distance IS NULL, 0, distance),
    IF(generator.registration_no IS NULL, '7000', generator.registration_no),
    IF(transporter.registration_no IS NULL, '700', transporter.registration_no),
    IF(city.name IS NULL, 'No City', city.name),
    IF(distance_rate IS NULL, 0, distance_rate),
    IF(plt_count IS NULL, 0, plt_count), IF(plt_weight IS NULL, 0, plt_weight),
    IF(mt_count IS NULL, 0, mt_count), IF(mt_weight IS NULL, 0, mt_weight),
    IF(ag_count IS NULL, 0, ag_count), IF(ag_weight IS NULL, 0, ag_weight),
    IF(ls_count IS NULL, 0, ls_count), IF(ls_weight IS NULL, 0, ls_weight)
    INTO OUTFILE 'collections.csv'
    FIELDS TERMINATED BY ','
    OPTIONALLY ENCLOSED BY '"'
    LINES TERMINATED BY '\n'
    FROM claim
    LEFT OUTER JOIN transportation_claim ON claim.id = transportation_claim.claim_id
    LEFT OUTER JOIN participant AS transporter ON transporter.id = transporter_id
    LEFT OUTER JOIN participant AS generator ON generator.id = generator_id
    LEFT OUTER JOIN city ON city.id = generator_city_id
    LEFT OUTER JOIN (
        SELECT SUM(count) AS plt_count, SUM(weight) AS plt_weight, transportation_claim_id
        FROM transportation_claim_detail
        INNER JOIN revenue_product ON revenue_product.id = revenue_product_id
        WHERE revenue_product.product_code = 'PLT'
        GROUP BY transportation_claim_id) AS plt ON plt.transportation_claim_id = transportation_claim.id
    LEFT OUTER JOIN (
        SELECT SUM(count) AS mt_count, SUM(weight) AS mt_weight, transportation_claim_id
        FROM transportation_claim_detail
        INNER JOIN revenue_product ON revenue_product.id = revenue_product_id
        WHERE revenue_product.product_code = 'MT'
        GROUP BY transportation_claim_id) AS mt ON mt.transportation_claim_id = transportation_claim.id
    LEFT OUTER JOIN (
        SELECT SUM(count) AS ag_count, SUM(weight) AS ag_weight, transportation_claim_id
        FROM transportation_claim_detail
        INNER JOIN revenue_product ON revenue_product.id = revenue_product_id
        WHERE revenue_product.product_code = 'AG'
        GROUP BY transportation_claim_id) AS ag ON ag.transportation_claim_id = transportation_claim.id
    LEFT OUTER JOIN (
        SELECT SUM(count) AS ls_count, SUM(weight) AS ls_weight, transportation_claim_id
        FROM transportation_claim_detail
        INNER JOIN revenue_product ON revenue_product.id = revenue_product_id
        WHERE revenue_product.product_code = 'LS'
        GROUP BY transportation_claim_id) AS ls ON ls.transportation_claim_id = transportation_claim.id
    WHERE claim_type = 'TI'
    ORDER BY CAST(claim_no AS UNSIGNED), CAST(IFNULL(form_no, '0') AS UNSIGNED)

【问题讨论】:

【参考方案1】:

您在问题中提供了很多信息。将文本数字排序为数字的一般解决方案是首先将它们转换为某种数字类型。考虑这个版本:

SELECT claim_no, form_no, ...
ORDER BY CAST(claim_no AS UNSIGNED), CAST(form_no AS UNSIGNED);

请注意,如果您持续/重复需要将 claim_noform_no 列用作数字,则可能意味着您的表格设计应更改为使这些列成为数字而不是文本。

【讨论】:

正如我在问题中展示的那样,我尝试了该解决方案(CASTing the fields)。它没有任何区别。我正在使用旧数据库,在关闭之前将数据拉入新系统,因此我不会费心更改字段类型 - 除非以某种方式使 ORDER BY 工作!

以上是关于MySQL order by - 排序不正确的主要内容,如果未能解决你的问题,请参考以下文章

MySQL 'Order By' - 正确排序字母数字

union 和order by 使用时排序不正确

MySQL order by的不同排序规则

MySQL order by的不同排序规则

mysql 数据操作 单表查询 查询排序: order by

Mysql(15)—Order By排序的底层原理与filesort排序