2010-03-26 5 views
0

J'ai une requête SQL avec trois instructions SELECT. Une image des tables de données générées par ces trois déclarations select est disponible sur www.britestudent.com/pub/1.png. Chacune des trois tables de données a des colonnes identiques. Je veux combiner ces trois tables dans une table de telle sorte que:Comment combiner trois instructions SELECT avec des exigences très complexes

(1) Toutes les lignes dans la table supérieure (Table1) sont toujours incluses.

(2) Les lignes de la table du milieu (Table2) sont incluses uniquement lorsque les valeurs des colonnes column1 (UserName) et column4 (CourseName) ne correspondent à aucune ligne de Table1. Les deux colonnes doivent correspondre pour que la ligne de la Table2 ne soit pas incluse.

(3) Les lignes de la table du bas (Table3) sont incluses uniquement lorsque la valeur de column4 (CourseName) ne figure déjà dans aucune ligne des résultats de la combinaison Table1 et Table2.

J'ai eu du succès dans la mise en œuvre (1) et (2) avec une requête SQL comme ceci:

SELECT DISTINCT 
    UserName AS UserName, 
    MAX(AmountUsed) AS AmountUsed, 
    MAX(AnsweredCorrectly) AS AnsweredCorrectly, 
    CourseName, 
    MAX(course_code) AS course_code, 
    MAX(NoOfQuestionsInCourse) AS NoOfQuestionsInCourse, 
    MAX(NoOfQuestionSetsInCourse) AS NoOfQuestionSetsInCourse 
FROM 
    ("SELECT statement 1" UNION "SELECT statement 2") dt_derivedTable_1 
GROUP BY CourseName, UserName 

Où "instruction SELECT 1" est la requête qui génère Table1 et "instruction SELECT 2" est la requête qui génère Table2. Une image de la table de données générée par cette requête est disponible sur www.britestudent.com/pub/2.png. Je peux m'en tirer avec la fonction MAX() car les valeurs des colonnes AmountUsed et AnsweredCorrectly de Table1 seront toujours plus grandes que celles de Table2 (et elles sont identiques dans les trois dernières colonnes des deux tables). Ce à quoi j'échoue est la mise en œuvre (3). Toute suggestion sur la façon de le faire sera appréciée. C'est difficile parce que les valeurs UserName dans Table3 sont null, et parce que les valeurs CourseName dans les résultats combinés Table1 et Table2 ne sont pas uniques (mais ils sont uniques dans Table3). Après l'implémentation (3), la table finale doit ressembler à la table de l'image 2.png avec l'ajout de la dernière ligne de Table3 (la ligne avec la valeur CourseName commençant par "4. Klasse ..."

J'ai essayé d'implémenter (3) en utilisant une autre table dérivée en utilisant SELECT, MAX() et UNION, mais je n'ai pas réussi à le faire fonctionner Voici ma requête SQL complète avec les lignes de cette tentative échouée) a commenté sur

Cheers, Frederick

PS -. Je suis nouveau sur ce forum (et de nouveau à SQL que nous ll), mais j'ai eu plus de réponses à mes précédents problèmes en lisant les messages d'autres personnes sur ce forum qu'en lisant tout autre forum ou site Web. Ce forum est une excellente ressource.

-- SELECT DISTINCT MAX(UserName), MAX(AmountUsed) AS AmountUsed, MAX(AnsweredCorrectly) AS AnsweredCorrectly, CourseName, MAX(course_code) AS course_code, MAX(NoOfQuestionsInCourse) AS NoOfQuestionsInCourse, MAX(NoOfQuestionSetsInCourse) AS NoOfQuestionSetsInCourse 
-- FROM (

       SELECT DISTINCT UserName AS UserName, MAX(AmountUsed) AS AmountUsed, MAX(AnsweredCorrectly) AS AnsweredCorrectly, CourseName, MAX(course_code) AS course_code, MAX(NoOfQuestionsInCourse) AS NoOfQuestionsInCourse, MAX(NoOfQuestionSetsInCourse) AS NoOfQuestionSetsInCourse 
       FROM (

         -- Table 1 - All UserAccount/Course combinations that have had quizzez. 
         SELECT DISTINCT dbo.win_user.user_name AS UserName, 
             cast(dbo.GetAmountUsed(dbo.session_header.win_user_id, dbo.course.course_id, dbo.course.no_of_questionsets_in_course) as nvarchar(10)) AS AmountUsed, 
             Isnull(cast(dbo.GetAnswerCorrectly(dbo.session_header.win_user_id, dbo.course.course_id, dbo.question_set.no_of_questions) as nvarchar(10)),0) AS AnsweredCorrectly, 
             dbo.course.course_name AS CourseName, 
             dbo.course.course_code, 
             dbo.course.no_of_questions_in_course AS NoOfQuestionsInCourse, 
             dbo.course.no_of_questionsets_in_course AS NoOfQuestionSetsInCourse 
         FROM   dbo.session_detail 
             INNER JOIN dbo.session_header ON dbo.session_detail.session_header_id = dbo.session_header.session_header_id 
             INNER JOIN dbo.win_user ON dbo.session_header.win_user_id = dbo.win_user.win_user_id 
             INNER JOIN dbo.win_user_course ON dbo.win_user_course.win_user_id = dbo.win_user.win_user_id 
             INNER JOIN dbo.question_set ON dbo.session_header.question_set_id = dbo.question_set.question_set_id 
             RIGHT OUTER JOIN dbo.course ON dbo.win_user_course.course_id = dbo.course.course_id 
         WHERE   (dbo.session_detail.no_of_attempts = 1 OR dbo.session_detail.no_of_attempts IS NULL) 
             AND (dbo.session_detail.is_correct = 1 OR dbo.session_detail.is_correct IS NULL) 
             AND (dbo.win_user_course.is_active = 'True') 
         GROUP BY  dbo.win_user.user_name, dbo.course.course_name, dbo.question_set.no_of_questions, dbo.course.no_of_questions_in_course, 
             dbo.course.no_of_questionsets_in_course, dbo.session_header.win_user_id, dbo.course.course_id, dbo.course.course_code 

        UNION ALL 

         -- Table 2 - All UserAccount/Course combinations that do or do not have quizzes but where the Course is selected for quizzes for that User Account. 
         SELECT   dbo.win_user.user_name AS UserName, 
             -1 AS AmountUsed, 
             -1 AS AnsweredCorrectly, 
             dbo.course.course_name AS CourseName, 
             dbo.course.course_code, 
             dbo.course.no_of_questions_in_course AS NoOfQuestionsInCourse, 
             dbo.course.no_of_questionsets_in_course AS NoOfQuestionSetsInCourse 
         FROM   dbo.win_user_course 
             INNER JOIN dbo.win_user ON dbo.win_user_course.win_user_id = dbo.win_user.win_user_id 
             RIGHT OUTER JOIN dbo.course ON dbo.win_user_course.course_id = dbo.course.course_id 
         WHERE   (dbo.win_user_course.is_active = 'True') 
         GROUP BY  dbo.win_user.user_name, dbo.course.course_name, dbo.course.no_of_questions_in_course, 
             dbo.course.no_of_questionsets_in_course, dbo.course.course_id, dbo.course.course_code 

       ) dt_derivedTable_1 

       GROUP BY CourseName, UserName 

--  UNION ALL 

      -- Table 3 - All Courses. 
--   SELECT DISTINCT null AS UserName, 
--       -2 AS AmountUsed, 
--       -2 AS AnsweredCorrectly, 
--       dbo.course.course_name AS CourseName, 
--       dbo.course.course_code, 
--       dbo.course.no_of_questions_in_course AS NoOfQuestionsInCourse, 
--       dbo.course.no_of_questionsets_in_course AS NoOfQuestionSetsInCourse 
--   FROM   dbo.course 
--   WHERE   is_active = 'True' 


-- ) dt_derivedTable_2 

-- GROUP BY CourseName 
-- ORDER BY CourseName 

Répondre

0

Voici une solution aromatisée Oracle:

Select 
     * 
    from table1 

    UNION 

    select 
     * 
    from table2 
    where not exists(
     select 'x' 
     from table1 
     where 
      table2.username = table1.username 
      and table2.coursename = table1.coursename 
    ) 

    UNION 

    select 
     * 
    from table3 
    where 
     coursename not in (
      Select 
       coursename             
      from table1 

      UNION /* the union operator implies distinct, so 
        there will be no duplicates */ 

      select 
       coursename 
      from table2 
      where not exists(
       select 'x' 
       from table1 
       where 
        table2.username = table1.username 
        and table2.coursename = table1.coursename 
      ) 
     ) 
+0

Mark, merci. Cela a fonctionné parfaitement! Je ne comprends pas la partie "select 'x'", mais cela fonctionne. Encore merci. À votre santé. – Frederick

+0

"existe" est un peu drôle. Il retournera "true" si la sous-requête a des lignes, et false s'il n'en a pas. Le contenu réel de la sous-requête n'a pas d'importance. Donc, sélectionnez 'x' provoquera l'affichage d'une colonne anonyme avec le contenu 'x' pour chaque ligne trouvée. C'est plus rapide que quelque chose comme select * car la base de données n'a pas réellement récupéré les lignes. C'est aussi plus rapide que "column not in (subquery)" pour la même raison. –

1

Avec ces exigences de filtrage (en fonction des rangées de requêtes précédentes), je recommande une variable de table.

DECLARE @MyTable TABLE 
(
    ID int PRIMARY KEY, 
    Name varchar(50), 
    QueryNumber int 
) 

INSERT INTO @MyTable (ID, Name, QueryNumber) 
SELECT CustomerID, CustomerName, 1 
FROM Customer 
WHERE Name = "Bob" 

INSERT INTO @MyTable (ID, Name, QueryNumber) 
SELECT CustomerID, CustomerName, 2 
FROM Customer 
WHERE Name = "Joe" and CustomerID not in (SELECT ID FROM @MyTable) 

INSERT INTO @MyTable (ID, Name, QueryNumber) 
SELECT CustomerID, CustomerName, 3 
FROM Customer 
WHERE CustomerID not in (SELECT ID FROM @MyTable) 

SELECT * 
FROM @MyTable 
+0

+1, vous pourriez avoir besoin de 'commande par QueryNumber' et quelques autres colonnes. Dans mes propres expériences, je trouve que la performance des variables de table diminue car vous avez plus de lignes dans la table. Si vous travaillez avec un grand nombre de lignes, essayez d'utiliser un #TempTable, et n'oubliez pas, vous pouvez créer un index si nécessaire aussi –

+0

Bonjour David, merci pour votre réponse. Cependant, je ne comprends pas l'approche que vous suggérez. Je ne sais pas ce que je mettrais à la place de "Name =" Bob "" et "Name =" Joe "".Je ne pense pas que je connaîtrai ces valeurs avant la fin de ma requête SQL. – Frederick

Questions connexes