2010-08-20 7 views
1

Quelqu'un change mon titre pour mieux refléter ce que j'essaie de lui demander.Sélectionner la sélection en fonction d'une priorité

J'ai une table comme

Table (id, value, value_type, data) 

ID est pas unique. Il n'y a pas de clé unique.

value_type a deux valeurs possibles, disons A et B.

type B est meilleur que A, mais souvent pas disponible.

Pour chaque id si un enregistrement avec EXISTE value_type B, je veux tous les enregistrements avec cet identifiant et value_type B.

Si aucun enregistrement pour cet identifiant avec value_type B existe, je veux tous les enregistrements avec cet identifiant et value_type A.

Notez que si B existe pour cet ID, je ne veux pas d'enregistrements de type A.

Je fais actuellement cela avec une série de tables temporaires. Y a-t-il une seule instruction select (sous-requêtes OK) qui peut faire le travail?

Merci beaucoup!

Détails supplémentaires:

SQL Server 2005

+0

quelle version de SQL Server? –

+0

SQL 2005 "partiton by" est disponible pour moi. J'ai essayé de l'utiliser, mais j'ai eu un problème avec quand il y avait plus d'un enregistrement avec le type B. – kralco626

Répondre

4

RANK, plutôt que ROW_NUMBER, parce que vous voulez des liens (ceux qui ont la même valeur B) pour avoir la même valeur de classement:

WITH summary AS (
    SELECT t.*, 
     RANK() OVER (PARTITION BY t.id 
          ORDER BY t.value_type DESC) AS rank 
    FROM TABLE t 
    WHERE t.value_type IN ('A', 'B')) 
SELECT s.id, 
     s.value, 
     s.value_type, 
     s.data 
    FROM summary s 
WHERE s.rank = 1 

Version non CTE:

SELECT s.id, 
     s.value, 
     s.value_type, 
     s.data 
    FROM (SELECT t.*, 
       RANK() OVER (PARTITION BY t.id 
           ORDER BY t.value_type DESC) AS rank 
      FROM TABLE t 
     WHERE t.value_type IN ('A', 'B')) s 
WHERE s.rank = 1 

WITH test AS (
    SELECT 1 AS id, 'B' AS value_type 
    UNION ALL 
    SELECT 1, 'B' 
    UNION ALL 
    SELECT 1, 'A' 
    UNION ALL 
    SELECT 2, 'A' 
    UNION ALL 
    SELECT 2, 'A'), 
    summary AS (
    SELECT t.*, 
      RANK() OVER (PARTITION BY t.id 
          ORDER BY t.value_type DESC) AS rank 
    FROM test t) 
SELECT * 
    FROM summary 
WHERE rank = 1 

je reçois:

id value_type rank 
---------------------- 
1 B   1 
1 B   1 
2 A   1 
2 A   1 
+0

+1 Battez-moi de 12 secondes! –

+1

@ Martin Smith: Alors vous l'admettez - vous * avez volé * ma réponse! =) –

+0

haha! Je faisais presque exactement ça avec ROW_NUMBER. Juste au moment où je pensais que je commençais à utiliser ces fonctions de "fenêtre". Merci! Je vais tester et vous faire savoir. – kralco626

0

Il utilise un syndicat, combinant tous les enregistrements de valeur B avec tous les enregistrements qui ont seulement les valeurs suivantes:

SELECT * 
FROM mainTable 
WHERE value_type = B 
GROUP BY value_type UNION SELECT * 
          FROM mainTable 
          WHERE value_type = A 
           AND id NOT IN(SELECT * 
              FROM mainTable 
              WHERE value_type = B); 
+0

Ceci est un doublon de la réponse de Beth, qui était le premier. – ErikE

0

Essayez cette (MSSQL).

Select id, value_typeB, null 
from myTable 
where value_typeB is not null 
Union All 
Select id, null, value_typeA 
from myTable 
where value_typeB is null and value_typeA is not null 
+0

value_typeB et value_typeA ne sont pas des colonnes. value_type est une colonne et elle peut avoir une valeur A ou B – kralco626

3
SELECT * 
    FROM table 
WHERE value_type = B 
UNION ALL 
SELECT * 
    FROM table 
WHERE ID not in (SELECT distinct id 
        FROM table 
        WHERE value_type = B) 
+0

Je pense que cette requête manquera les identifiants d'enregistrement qui sont seulement associés aux valeurs de type A. – dvanaria

+0

@dvanaria: Relisez la 2ème moitié de la déclaration UNION –

+0

@Beth: +1: Ne devrait pas besoin de la DISTINCT, pour l'amour du 'IN' –

1
declare @test as table(
id int , value [nvarchar](255),value_type [nvarchar](255),data int) 

INSERT INTO @test 
SELECT 1, 'X', 'A',1 UNION 
SELECT 1, 'X', 'A',2 UNION 
SELECT 1, 'X', 'A',3 UNION 
SELECT 1, 'X', 'A',4 UNION 
SELECT 2, 'X', 'A',5 UNION 
SELECT 2, 'X', 'B',6 UNION 
SELECT 2, 'X', 'B',7 UNION 
SELECT 2, 'X', 'A',8 UNION 
SELECT 2, 'X', 'A',9 


SELECT * FROM @test x 
INNER JOIN 
(SELECT id, MAX(value_type) as value_type FROM 
@test GROUP BY id) as y 
ON x.id = y.id AND x.value_type = y.value_type 
+0

Votre requête est proche de la meilleure des requêtes soumises (souvent la meilleure). – ErikE

+0

@Emtucifor - Je crois, cependant, que si vous remplaciez @test pour une sous-requête, ou une vue non-indexée, cela prendrait plus de temps car il faudrait l'appeler deux fois. Ce serait de ma faute cependant, pas de Chris parce que je n'ai pas dit cela dans ma question – kralco626

+0

@ kralco626 - Le nombre de fois qu'une table est mentionnée dans une requête ne correspond pas à la performance. Mettre l'accent sur la réduction du nombre de fois que le nom de la table est mentionné, sans regarder les performances réelles de la requête (CPU et lectures) va certainement vous conduire sur le mauvais chemin. – ErikE

0

Peut-être quelque chose comme ceci:

select * from mytable 
where id in (select distinct id where value_type = "B") 
union 
select * from mytable 
where id in (select distinct id where value_type = "A" 
and id not in (select distinct id where value_type = "B")) 
2

La plus courte requête pour faire le travail que je peux penser:

SELECT TOP 1 WITH TIES * 
FROM #test 
ORDER BY Rank() OVER (PARTITION BY id ORDER BY value_type DESC) 

Il s'agit 50% pire sur CPU que les solutions OMG Ponies et Christoperous 5000, mais le même nombre de lectures . C'est le genre supplémentaire qui fait qu'il faut plus de CPU.

La plus performante requête originale que je suis venu avec à ce jour est:

SELECT * 
FROM #test 
WHERE value_type = 'B' 
UNION ALL 
SELECT * 
FROM #test T1 
WHERE NOT EXISTS (
    SELECT * 
    FROM #test T2 
    WHERE 
     T1.id = T2.id 
     AND T2.value_type = 'B' 
) 

Cela bat constamment tous les autres présentés sur le processeur d'environ 1/3 (les autres sont environ 50% de plus) mais a 3x le nombre de lectures. La durée de cette requête est souvent 2/3rds le temps de tous les autres. Je considère que c'est un bon concurrent.

Les index et les types de données peuvent tout changer.

+0

Merci de faire l'analyse, bon travail. Cependant, j'ai besoin d'une solution qui n'utilise la table qu'une seule fois. – kralco626

+0

aussi je pense que vos solutions ne retourneraient qu'un seul enregistrement. Que faire s'il y a plus d'un enregistrement avec le type de priorité le plus élevé? – kralco626

+0

Essayez la requête :) – ErikE

Questions connexes