4

与下表类似的数据:

|  id  |    e_date    |  e_time |  place | person| ref  |ref_type| 
|  10  |  2015-08-03  |  10:30  |  work  |  tony | 1234 |   A    |
|  25  |  2015-08-03  |  10:30  |  work  |  NULL | NULL |   A    |
|  37  |  2015-08-03  |  NULL   |  work  |  tony | NULL |   A    |
|  99  |  2015-08-03  |  10:30  |  work  |  fred | 1234 |   B    |

在 MySQL子句 中获得一系列条件(具有降序重要性)的第一个匹配项的最佳方法是什么?WHERE

  • 匹配ref字段
  • 如果字段中没有匹配ref项,则匹配e_date+e_time+place字段
  • 如果没有匹配refe_date+e_time+place然后匹配e_date+place+person

此处的目的是获得最佳单行匹配 - 基于一系列递减的标准 - 并且仅在不满足前面的标准时使用该标准。

我第一次尝试查询看起来像:

SELECT id FROM my_table 
WHERE ref_type = 'A' AND (
  ref = '1234'
  OR
  (e_date = '2015-08-03' AND e_time = '10:30' AND place = 'work')
  OR
  (e_date = '2015-08-03' AND place = 'work' AND person = 'tony')
)

但由于OR是包容性的(不是顺序的),这会返回 rows 1025并且37- 见这个 sqlfiddle

我可以ORDER BY ref DESC并且LIMIT 1(如果我修改为SELECT id, ref FROM...),但如果我没有ref价值并且必须通过第二个或第三个条件中的任何一个来区分,那对我没有帮助

IF我的下一次尝试在WHERE子句中使用嵌套条件,例如:

SELECT id FROM my_table 
WHERE ref_type = 'A' AND (
  IF(ref = 1234, 
    ref = 1234,
    IF(e_date = '2015-08-03' AND e_time = '10:30' AND place = 'work', 
       e_date = '2015-08-03' AND e_time = '10:30' AND place = 'work',
       e_date = '2015-08-03' AND place = 'work' AND person = 'tony'
    )
  )
)

但是,这也会返回 rows 1025并且37- 请参阅此 sqlfiddle

还尝试使用IFNULL

SELECT id FROM my_table 
WHERE ref_type = 'A' AND 
  IFNULL(ref = '1234', 
    IFNULL(e_date = '2015-08-03' AND e_time = '10:30' AND place = 'work',
      e_date = '2015-08-03' AND place = 'work' AND person = 'tony')
  )

返回行1025 - 看到这个 sqlfiddle

编写此查询的最佳方法是什么?

我正在使用 php——我可以运行 3 个单独的顺序查询并对每个结果使用 php 条件——但我想使用单个 db 查询,因为该代码每小时运行数百万次。

4

4 回答 4

3

唯一的方法(据我所知)是使用不同的where. 您可以使用联合作为单个查询来执行此操作:

SELECT * FROM (
    SELECT <stuff> FROM <table> WHERE <most important condition> LIMIT 1
    UNION
    SELECT <stuff> FROM <table> WHERE <less important condition> LIMIT 1
    UNION
    SELECT <stuff> FROM <table> WHERE <even less important condition> LIMIT 1
    UNION
    ...
) as t
LIMIT 1

或者你可以一个一个地运行它们,如果有结果就停止。

于 2015-07-07T09:27:32.763 回答
1

尝试这个 :

SELECT
    id
FROM my_table
WHERE ref_type = 'A'
ORDER BY
(CASE WHEN ref = '1234' 
    THEN 1
    ELSE 
        (CASE WHEN e_date = '2015-08-03' AND e_time = '10:30' AND place = 'work' 
            THEN 2
            ELSE
                (CASE WHEN (e_date = '2015-08-03' AND place = 'work' AND person = 'tony')
                    THEN 3
                    ELSE 4
                END)
        END)
END)
LIMIT 1

或者,如果它的运行时间很长,则使用以下查询创建一个视图:

SELECT
    id,
    (CASE WHEN ref = '1234' 
        THEN 1
        ELSE 
            (CASE WHEN e_date = '2015-08-03' AND e_time = '10:30' AND place = 'work' 
                THEN 2
                ELSE
                    (CASE WHEN (e_date = '2015-08-03' AND place = 'work' AND person = 'tony')
                        THEN 3
                        ELSE 4
                    END)
            END)
    END) AS "case_field"
FROM my_table
WHERE ref_type = 'A'

然后运行:

SELECT id FROM your_view ORDER BY case_field LIMIT 1;
于 2015-07-07T09:35:12.977 回答
0

你应该试试case when in where,

SELECT id FROM my_table 
WHERE 
1 = case when ref_type = 'A' then 1 
    else
        case when ref = '1234' then 1 
        else
            case when (e_date = '2015-08-03' AND e_time = '10:30' AND place = 'work') or (e_date = '2015-08-03' AND place = 'work' AND person = 'tony') then 1 else 0 end 
        end
    end 

如果任何一个满足条件,它将以升序方式检查条件,然后它不会检查更多。

不确定您的确切条件,所以只是放在这里作为参考,您应该使用这种方式来满足您的需求。

于 2015-07-07T10:07:36.693 回答
0

ref如果我理解你的逻辑,如果它不为空,你想返回该行。如果它为空,那么e_time不为空的那个。最后,一个person不为空的。那么,也许您可​​以将此排序用于您的第一次尝试查询?

SELECT id FROM my_table 
WHERE ref_type = 'A' AND (
  ref = '1234'
  OR
  (e_date = '2015-08-03' AND e_time = '10:30' AND place = 'work')
  OR
  (e_date = '2015-08-03' AND place = 'work' AND person = 'tony')
)
ORDER BY ref DESC,
  e_time DESC,
  person DESC 
LIMIT 1 ;
于 2015-07-07T09:37:31.027 回答