2008-09-29 6 views
3

Le problème en lui-même est simple, mais je ne peux pas trouver une solution qui le fasse en une seule requête, et voici mon "abstraction" du problème pour permettre une explication plus simple:Sélection de lignes uniques dans un ensemble de deux possibilités

Je laisserai mon stand original explenation, mais voici un ensemble de données d'échantillons et le résultat i attendre:

Ok, donc voici quelques exemples de données, je me suis séparé paires par une ligne blanche

------------- 
| Key | Col | (Together they from a Unique Pair) 
-------------- 
| 1  Foo | 
| 1  Bar | 
|   | 
| 2  Foo | 
|   | 
| 3  Bar | 
|   | 
| 4  Foo | 
| 4  Bar | 
-------------- 

Et le résultat t Je me attends, après l'exécution de la requête une fois, il faut pouvoir sélectionner ce jeu de résultats dans une requête:

1 - Foo 
2 - Foo 
3 - Bar 
4 - Foo 

explenation Original:

J'ai une table, appelez TABLE où j'ai un deux colonnes dire ID et NAME qui forment ensemble la clé primaire de la table. Maintenant, je veux sélectionner quelque chose où ID=1 puis vérifie d'abord s'il peut trouver une ligne où NAME a la valeur "John", si "John" n'existe pas, il devrait chercher une ligne où NAME est "Bruce" - mais seulement retourner "John" si "Bruce" et "John" existent ou seulement "John" existe bien sûr. Notez également qu'il devrait être capable de retourner plusieurs lignes par requête qui correspondent aux critères ci-dessus, mais avec des combinaisons ID/Nom différentes bien sûr, et que l'explication ci-dessus est juste une simplification du vrai problème.

Je pourrais être complètement aveuglé par mon propre code et ma ligne de pensée, mais je n'arrive pas à comprendre cela.

+0

Pourriez-vous s'il vous plaît clarifier cette question en donnant un Exemple? c'est-à-dire donner des ensembles de données minimaux et les solutions que vous souhaitez obtenir d'eux? – Georgi

+0

Comment cross platform avez-vous besoin de la réponse? Je pourrais trouver quelque chose qui fonctionnerait dans SQL Server, mais pas dans MySQL ... –

+0

Georgi: J'ai clarifié l'exemple avec quelques exemples de données et le résultat que je m'attendrais après une requête AviewAnew: Tout est utile – thr

Répondre

4

Ceci est assez similaire à ce que vous avez écrit, mais devrait être assez rapide que EXISTE PAS est plus efficace, dans ce cas, que NOT IN ...

mysql> select * from foo; 
+----+-----+ 
| id | col | 
+----+-----+ 
| 1 | Bar | 
| 1 | Foo | 
| 2 | Foo | 
| 3 | Bar | 
| 4 | Bar | 
| 4 | Foo | 
+----+-----+ 

SELECT id 
    , col 
    FROM foo f1 
WHERE col = 'Foo' 
    OR (col = 'Bar' AND NOT EXISTS(SELECT * 
            FROM foo f2 
            WHERE f1.id = f2.id 
             AND f2.col = 'Foo' 
           ) 
    ); 

+----+-----+ 
| id | col | 
+----+-----+ 
| 1 | Foo | 
| 2 | Foo | 
| 3 | Bar | 
| 4 | Foo | 
+----+-----+ 
-1

Vous pouvez utiliser les jointures au lieu de existe et cela peut améliorer le plan de requête dans les cas où l'optimiseur est pas assez intelligent:

SELECT f1.id 
    ,f1.col 
FROM foo f1 
LEFT JOIN foo f2 
    ON f1.id = f2.id 
    AND f2.col = 'Foo' 
WHERE f1.col = 'Foo' 
    OR (f1.col = 'Bar' AND f2.id IS NULL) 
0

essayez ceci:

select top 1 * from (
SELECT 1 as num, * FROM TABLE WHERE ID = 1 AND NAME = 'John' 
union 
SELECT 2 as num, * FROM TABLE WHERE ID = 1 AND NAME = 'Bruce' 
) t 
order by num 
1

Vous pouvez rejoindre le table initiale à lui-même avec un OUTER JOIN comme ceci:

create table #mytest 
    (
    id   int, 
    Name   varchar(20) 
    ); 
go 

insert into #mytest values (1,'Foo'); 
insert into #mytest values (1,'Bar'); 
insert into #mytest values (2,'Foo'); 
insert into #mytest values (3,'Bar'); 
insert into #mytest values (4,'Foo'); 
insert into #mytest values (4,'Bar'); 
go 

select distinct 
    sc.id, 
    isnull(fc.Name, sc.Name) sel_name 
from 
    #mytest sc 

    LEFT OUTER JOIN #mytest fc 
     on (fc.id = sc.id 
      and fc.Name = 'Foo') 

comme cela.

+0

i semble avoir une erreur en essayant cela dans mysql, postgre seulement? – thr

+0

Je l'ai testé dans SQL Server 2005. –

-1

Dans PostgreSQL, je crois que ce serait ceci:

SELECT DISTINCT ON (id) id, name 
FROM mytable 
ORDER BY id, name = 'John' DESC; 

Mise à jour - fausses sortes avant vrai - je l'avais à l'origine en arrière. Notez que DISTINCT ON est une fonctionnalité PostgreSQL et ne fait pas partie du langage SQL standard. Qu'est-ce qui se passe ici, c'est qu'il ne vous montre que la première rangée pour un identifiant donné qu'il rencontre. Puisque nous commandons par temps le nom de John, les lignes nommées John seront choisies parmi tous les autres noms.

Avec votre deuxième exemple, il serait:

SELECT DISTINCT ON (key) key, col 
FROM mytable 
ORDER BY key, col = 'Foo' DESC; 

Cela vous donnera:

1 - Foo 
2 - Foo 
3 - Bar 
4 - Foo 
0

je suis venu avec moi-même solution, mais il est un peu lent et complexe - ni ne elle s'étend bien aux requêtes plus avancées:

SELECT * 
FROM users 
WHERE name = "bruce" 
OR (
    name = "john" 
    AND NOT id 
    IN (
     SELECT id 
     FROM posts 
     WHERE name = "bruce" 
    ) 
) 

Aucune alternative sans jointures lourdes, etc.?

0

Ok, donc voici quelques exemples de données, je me suis séparé paires par une ligne blanche

------------- 
| Key | Col | (Together they from a Unique Pair) 
-------------- 
| 1  Foo | 
| 1  Bar | 
|   | 
| 2  Foo | 
|   | 
| 3  Bar | 
|   | 
| 4  Foo | 
| 4  Bar | 
-------------- 

Et le résultat que je me attends:

1 - Foo 
2 - Foo 
3 - Bar 
4 - Foo 

Je ne réglaient-dessus, mais cette requête est horriblement inefficace pour les tables lager, d'une autre manière?

0

Voici un exemple qui fonctionne dans SQL Server 2005 et plus tard. C'est un modèle utile où vous voulez choisir la rangée supérieure (ou n premières lignes) en fonction d'un ordre personnalisé. Cela vous permettra non seulement de choisir parmi deux valeurs avec des priorités personnalisées, mais n'importe quel nombre. Vous pouvez utiliser la fonction ROW_NUMBER() et une expression CASE:

CREATE TABLE T (id int, col varchar(10)); 

INSERT T VALUES (1, 'Foo') 
INSERT T VALUES (1, 'Bar') 
INSERT T VALUES (2, 'Foo') 
INSERT T VALUES (3, 'Bar') 
INSERT T VALUES (4, 'Foo') 
INSERT T VALUES (4, 'Bar') 

SELECT id,col 
FROM 
(SELECT id, col, 
    ROW_NUMBER() OVER (
    PARTITION BY id 
    ORDER BY 
    CASE col 
    WHEN 'Foo' THEN 1 
    WHEN 'Bar' THEN 2 
    ELSE 3 END 
    ) AS RowNum 
    FROM T 
) AS X 
WHERE RowNum = 1 
ORDER BY id 
1

Pas besoin de faire de ce trop complexe, vous pouvez simplement utiliser MAX() et group by ...

select id, max(col) from foo group by id 
Questions connexes