2011-02-17 9 views
8

voglio cercare i record in cui un campo particolare o inizia con qualche stringa (diciamo "ar") o quel campo contiene la stringa, "ar".condizioni calibrati nella clausola WHERE di un'istruzione SQL

Tuttavia, considero le due condizioni diverse, perché sto limitando il numero di risultati restituiti a 10 e voglio che la condizione STARTS WITH sia ponderata più pesantemente della condizione CONTAINS.

Esempio:

SELECT * 
FROM Employees 
WHERE Name LIKE 'ar%' OR Name LIKE '%ar%' 
LIMIT 10 

Il problema è che è che se ci sono nomi che iniziano con "ar" dovrebbero essere favoriti. L'unico modo in cui dovrei recuperare un nome che CONTIENE solo "ar" è se ci sono meno di 10 nomi che iniziano con "ar"

Come posso farlo contro un database MySQL?

risposta

10

È necessario selezionarli in 2 parti, e aggiungere un tag preferenza ai risultati. 10 da ciascun segmento, poi unirle e riprendere la migliore 10. Se il segmento 1 produce 8 iscrizioni, allora segmento 2 di UNION ALL prodotto volontà il restante 2

SELECT * 
FROM 
(
SELECT *, 1 as Preferred 
FROM Employees 
WHERE Name LIKE 'ar%' 
LIMIT 10 
UNION ALL 
SELECT * 
FROM 
(
    SELECT *, 2 
    FROM Employees 
    WHERE Name NOT LIKE 'ar%' AND Name LIKE '%ar%' 
    LIMIT 10 
) X 
) Y 
ORDER BY Preferred 
LIMIT 10 
+0

+1. D'accordo, questo è più efficiente. – mellamokb

+0

Questo sembra funzionare per me. Puoi spiegarlo a tutti?Capisco (in generale) cosa significa unione. Quali sono X e Y e qual è il, 1 e, 2? –

+1

@Stephen - X e Y sono solo alias assegnati alle sottoquery (ciascuna tabella derivata deve essere aliasata, in modo che possano essere referenziate se necessario). È possibile aggiungere una colonna a qualsiasi set di risultati, semplicemente assegnando un valore e denominando (aliasing) la colonna. In questo caso, ho aggiunto una colonna denominata "Preferred" con un valore di 1 nella prima parte e un valore di 2 nella seconda parte. Le colonne ora fanno parte del risultato (per l'interrogazione). – RichardTheKiwi

4

Assegnare un valore di codice ai risultati, e ordinare per il valore del codice:

select 
    *, 
    (case when name like 'ar%' then 1 else 2 end) as priority 
from 
    employees 
where 
    name like 'ar%' or name like '%ar%' 
order by 
    priority 
limit 10 

Edit:

See Richard aka cyberkiwi risposta per una soluzione più efficiente se ci sono un sacco di partite potenzialmente .

+0

Se la tabella contiene molte partite , quindi questo può essere più lento poiché deve prima assegnare una priorità a tutte le partite. – RichardTheKiwi

0

Prova questo (non hanno un'istanza MySQL immediatamente disponibile per testare con):

SELECT * FROM 
    (SELECT * FROM Employees WHERE Name LIKE 'ar%' 
    UNION 
    SELECT * FROM Employees WHERE Name LIKE '%ar%' 
    ) 
LIMIT 10 

ci sono modi probabilmente meglio da fare, ma che balzò alla mente.

0

mia soluzione è:

SELECT * 
FROM Employees 
WHERE Name LIKE '%ar%' 
ORDER BY instr(name, 'ar'), name 
LIMIT 10 

instr() cerca la prima occorrenza del modello in questione. AR% arriverà prima di xxAR.

Ciò impedisce:

  1. dovrebbe fare solo scansione della tabella 1 volta. I sindacati e le tabelle derivate fanno 3. I primi due sulle colonne per filtrare i pattern e poi il terzo sul sottoinsieme per trovare dove sono uguali - poiché l'unione filtra i duplicati.

  2. dà una vera sorta in base alla posizione del pattern. WX> xW> XXW> ecc ...

0
SELECT * 
FROM Employees 
WHERE Name LIKE 'ar%' OR Name LIKE '%ar%' 
ORDER BY LIKE 'ar%' DESC 
LIMIT 10 

Dovrebbe funzionare gli ordini dal binario vero/falso per come e se index'ed dovrebbero beneficiare l'indice