2014-05-09 1 views
0

je tableaux ci-dessousoracle 11g pivot optimisation des requêtes - plusieurs lignes à simple rangée

user table 
USER_ID USER_NAME 
1   smith 
2   clark 
3   scott 
4   chris 
5   john 

property table 
P_ID PROPERTY 
1  first_name 
2  last_name 
3  age 
4  skill 

user_property table 
PV_ID USER_ID P_ID VALUE 
1  1   1 Smith 
2  1   2 A 
3  1   3 34 
4  1   4 Java 
5  1   4 DB 
6  2   1 Clark 
7  2   2 B 
8  2   3 39 
9  2   4 Java 
10  2   4 net 
11  2   4 linux 
12  3   1 Scott 
13  3   2 C 
14  3   3 31 

Je veux écrire une requête qui récupérer les données de tous les tableaux ci-dessus comme ci-dessous: (Savoir-faire sera la première compétence pour ce utilisateur si disponible sinon null)

USER_ID USER_NAME FIRST_NAME LAST_NAME SKILL 
1  smith  Smith  A   Java 
2  clark  Clark  B   Java 
3  scott  Scott  C   null 

J'ai essayé comme ci-dessous, mais obtenir problème de performance:

SELECT 
    u.user_id, 
    u.user_name, 
    MAX(DECODE(p.property, 'first_name', text_value)) firstName, 
    MAX(DECODE(p.property, 'last_name', text_value)) lastName, 
    MAX(DECODE(p.property, 'age', text_value)) age, 
    MAX(DECODE(p.property, 'skill', text_value)) skill 
FROM user u, 
    property p, 
    user_property up, 
WHERE u.user_id = up.user_id 
AND p.p_id = up.p_id 
GROUP BY u.user_id, 
    u.user_name; 

Comment pourrais-je écrire cela comme requête optimisée pour Oracle 11g.

+0

Alors combien de lignes avez-vous dans chaque table? – Mihai

Répondre

0

Les performances de votre requête dépendent de la taille de la table et des index que vous avez dans ces tables. Dans la plupart des cas, il est préférable d'avoir un index sur chaque clé primaire et étrangère. L'index sur la clé primaire est un must de toute façon. L'index sur la clé étrangère accélère les jointures et empêche les verrous de table lorsque vous supprimez des lignes.

Une alternative à votre requête serait d'utiliser plusieurs jointures au lieu de sous-requêtes et d'utiliser la clause WITH pour simplifier:

with t as (
    select u.user_id, u.user_name, up.p_id, up.value 
    from user_property up 
    join user u on u.user_id = up.user_id 
) 
select u.user_id, u.user_name, 
    t_first_name.value first_name, 
    t_last_name.value last_name, 
    (select min(value) from t where t.user_id = u.user_id and t.p_id = 4) skill 
from user u 
left join t t_first_name on t_first_name.user_id = u.user_id and t_first_name.p_id = 1 
left join t t_last_name on t_last_name.user_id = u.user_id and t_last_name.p_id = 2; 

BTW: Il est un modèle de données qui ne sont pas bien adaptés pour SQL. J'espère que ces propriétés d'utilisateur sont l'exception et le reste de la base de données a une conception plus propre.

+0

La table de propriétés contient 286 enregistrements, la table utilisateur contient 115838 enregistrements et la propriété user_property a 3221472 enregistrements. Toutes les tables ont des index sur PK. – KNale

+0

** table de propriétés a 286 enregistrements ** (dans ce que nous sommes intéressés par environ 45 propriétés, signifie 45 colonnes dans la clause de sélection de sortie), table de l'utilisateur a 115838 enregistrements et user_property a 3221472 enregistrements. Toutes les tables ont des index sur PK. De plus, min/max peut avoir un impact sur les performances car nous avons environ 45 colonnes. – KNale

+0

Voulez-vous que la sortie finale inclue une ligne pour chacun des 115 838 utilisateurs? Ou êtes-vous intéressé par un sous-ensemble des utilisateurs? – Torino

0

J'ai essayé ci-dessous la requête mais obtenir le produit cartésien.

with t as (
select u.user_id, u.user_name, up.p_id, up.value 
from user_property up 
join user u on u.user_id = up.user_id 
where u.user_name = 'smith' 
) 
select u.user_id, u.user_name, 
t_first_name.value first_name, 
t_last_name.value last_name, 
(select min(value) from t where t.user_id = u.user_id and t.p_id = 4) skill 
from user u 
left join t t_first_name on t_first_name.user_id = u.user_id and t_first_name.p_id = 1 
left join t t_last_name on t_last_name.user_id = u.user_id and t_last_name.p_id = 2; 

si j'exercerai ci-dessous demande que je reçois 5 comme mentionné dans mon exemple ci-dessus (Comme il y a 5 lignes dans ma table de user_property pour user_id 1 dans l'exemple)

select count(u.user_id) 
from user_property up 
join user u on u.user_id = up.user_id 
where u.user_name = 'smith' 

Par conséquent, si j'exécute ci-dessous requête Je compte comme 3 comme il ya 3 lignes dans mon exemple de table d'utilisation

with t as (
select u.user_id, u.user_name, up.p_id, up.value 
from user_property up 
join user u on u.user_id = up.user_id 
where u.user_name = 'smith' 
) 
select count(u.user_id) 
from user u 
left join t t_first_name on t_first_name.user_id = u.user_id and t_first_name.p_id = 1 
left join t t_last_name on t_last_name.user_id = u.user_id and t_last_name.p_id = 2; 
+0

Pourquoi avez-vous ajouté ceci comme réponse et pas mis à jour votre question originale? Y a-t-il une réputation minimale requise pour éditer sa propre question? – Torino

+0

Je ne comprends pas pourquoi vous obtenez un produit cartésien. Ma requête est correcte: voir ceci [violon] (http://sqlfiddle.com/#!4/b3495e/3). – Torino

+0

@Torino: Merci pour les mises à jour. Son travail de travail. Howerver origical query prend 145 secondes et cette nouvelle requête prend 130 secondes à exécuter. – KNale