MySql multiple where条件 - 仅匹配第一个正确的条件

时间:2015-07-07 09:17:50

标签: mysql sql if-statement ifnull

数据类似于下表:

|  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包含(不是顺序),因此会返回行102537 - 请参阅this sqlfiddle

我可以ORDER BY ref DESCLIMIT 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'
    )
  )
)

但是,这也会返回行102537 - 请参阅this 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 - 请参阅this sqlfiddle

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

我使用php - 我可以运行3个单独的顺序查询并在每个结果上使用php条件 - 但我想使用单个数据库查询,因为数百万次运行此代码每小时。

4 个答案:

答案 0 :(得分:2)

唯一的方法(我知道)是使用不同的where多次运行查询。您可以使用union来执行单个查询:

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

或者您可以逐个运行它们,如果有结果则停止。

答案 1 :(得分:0)

试试这个:

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;

答案 2 :(得分:0)

如果我理解了你的逻辑,你想要返回ref行,如果它不是空的话。如果它为null,那么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 ;

答案 3 :(得分:0)

你应该在何处尝试案例,

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 

如果符合标准的任何一个条件,它将以递增的方式检查条件,然后它不会检查更多。

不确定您的确切条件,以便在此处作为参考,您应该使用这种方式来满足您的需求。

相关问题