使用适当的SELECT语句从多个表中提取数据

时间:2019-04-06 23:44:27

标签: sql postgresql

我正在尝试使用SQL查询从已经创建的不同表中提取一些数据。表格如下:

CREATE TABLE Candidates 
(
    Candidate_ID INTEGER PRIMARY KEY,
    Start_Year DATE,
    Course_Current_Status VARCHAR(18),  
    First_Name TEXT,
    Last_Name TEXT,
    Gender TEXT);



CREATE TABLE Subjects 
(
    Subject_Code INTEGER PRIMARY KEY,
    Subject_Name TEXT, 
    Subject_Credits INTEGER,
    Subject_Level INTEGER,
    TeacherID INTEGER REFERENCES Teachers(TeacherID)
);

CREATE TABLE Subjects-taken 
(
    Marks_Obtained INTEGER,
    Subject_Code INTEGER REFERENCES Subjects(Subject_Code),
    Candidate_ID INTEGER REFERENCES Students(Candidate_ID),
    Program_Year_When_Enrolled TEXT, 
    PRIMARY KEY(Subject_Code, Candidate_ID)
);

我想在查询中显示三列:

Candidate_ID, Total_first_Year_Marks, Total_fourth_Year_Marks, Overall_Marks 

第四年标志着入学人数。Program_Year_When_Enrolled='Second' 第四年是Enrollment.Program_Year_When_Enrolled ='Third'。 对于总分数,查询必须创建一个新列,即总分数,即第二年分数的1/3和第三年分数的2/3。

选择         Candidates.Candidate_ID,         AVG(注册。成绩_获得)AS平均成绩     从         候选人     左联接         注册Candidates.Candidate_ID = Enrollment.Candidate_ID     哪里         Enrollment.Program_Year_When_Enrolled ='Third'         AND Candidates.Course_Current_Status ='已毕业'     通过...分组         Candidates.Candidate_ID

有人可以告诉我我做错了什么吗?非常感激。谢谢。

3 个答案:

答案 0 :(得分:1)

问题

您需要学习和理解什么是普通的 SQL子查询

  • 有两种类型:
    • 标量子查询(不相关)
    • 相关子查询
  • 可以在任何可以使用表达式的地方使用子查询。
  • 这都是自SQL ANSI 1987以来在ANSI SQL中提供的基本,必不可少的功能。

  • 这是相关子查询的当前条目:

  

ISO / IEC 9075-2:2008(E),第1262页(附件F):

     

45个E061-13相关子查询
  —第8.1节,/ predicate /:
  在/ table子查询中可以使用/ correlation name /时
  作为外部查询中列的相关引用

注意事项

此类型所需的类型为“相关”。

想象一下外部网格(如电子表格中的网格)。行是Student_ID,列是您要求的三列。普通或单个SELECT意味着这些列与行标识符(Student_ID)具有完全相同的关系。您试图将所有这些都放在一个SELECT中,却没有意识到电子表格中的每一列都与行标识符具有离散且不同的关系。

此外,您使用GROUP BY使事情变得复杂(使您感到困惑),这当然会影响整个SELECT(因为您只有一个):GROUP BY是必需的AVG(),而不是外部查询。

解决方案

首先,您需要一个子查询,因为这些列彼此不相关(它们与行标识符有关)。

第二,您需要一个相关子查询,该子查询将外部网格中的行标识符(Student_ID)(即外部查询)与该内部查询列的内容进行关联

  
      
  • 这是开发人员在摆弄SELECT时试图“使其起作用”的一个典型错误,因为他们没有意识到问题在于他们的理解和方法,即需要子查询。
  •   

代码

SELECT  -- Student_ID,  -- humans don"t use numbers for identifiers
        Last_Name,
        First_Name,
        Course_Code,
        (SELECT  AVG( Marks_Obtained ) -- CS.A
            FROM Enrollment 
            WHERE Student_ID = S_OUTER.Student_ID
                AND Module_Year = "Second" 
            GROUP BY Enrollment.Student_ID
            ) AS Avg_2nd_Year_Marks,
        (SELECT  AVG( Marks_Obtained ) -- CS.B
            FROM Enrollment 
            WHERE Student_ID = S_OUTER.Student_ID
                AND Module_Year = "Third" 
            GROUP BY Enrollment.Student_ID
            ) AS Avg_3rd_Year_Marks,
        (SELECT  AVG( Marks_Obtained ) -- CS.A / 3 * 1
            FROM Enrollment 
            WHERE Student_ID = S_OUTER.Student_ID
                AND Module_Year = "Second" 
            GROUP BY Enrollment.Student_ID
            ) / 3 + 
        (SELECT  AVG( Marks_Obtained ) -- CS.B / 3 * 2
            FROM Enrollment 
            WHERE Student_ID = S_OUTER.Student_ID
                AND Module_Year = "Third" 
            GROUP BY Enrollment.Student_ID
             ) / 3 * 2 AS Overall_Marks
    FROM Students S_OUTER 
    WHERE Graduate_Year = "2017"  -- get current year using relevant date function
    ORDER BY Last_Name, First_Name

说明和问题解答

  1. CS表示 C 或相关的 S ubquery。外部查询(别名 S_OUTER)和内部查询之间的关联是通过别名实现的。

  2. 子查询的产品是虚拟(非真实)列,该列在命令执行期间一直存在。它是由SELECT计算或生成的(与 created 不同)。这样的列(数据库中不存在,是动态生成的)需要一个列名,例如以下。它将出现在结果集中的客户端程序中:

    • Avg_2nd_Year_Marks
    • Avg_3rd_Year_Marks
    • Overall_Marks
  3. 第三列必须重复两个子查询,因为它必须执行算术。

  4. 该代码使用标准的ANSI SQL工具(例如,子查询)和语法,它将在任何与SQL兼容的平台上运行(不需要CTE等)

    • Oracle将自身放在子查询上(这不是SQL投诉)。
    • 不知道诸如postGresNONsql的免费软件是否提供普通的SQL子查询。众所周知,它与SQL不兼容,这与市场营销和手册每一页中的“ SQL兼容”声明相反。大规模欺诈。该站点上大多数搜索者没有看到子查询的事实表明该免费软件没有子查询。
    • 如果它没有基本的SQL子查询工具,则可以使用一种方法将子查询代码折磨到内联视图中,然后使用GROUP BY对其进行锤击,以获得相似的结果。效率很低,但是,嘿,“平台”不是平台,绝不是服务器,它已经非常低效且缓慢。
  

我应该在表中创建一个名称为Avg_3rd_Year_Marks的列

绝对不是。

  

或者是否有其他代码可以解决此问题

是的。从这个答案的开始就规定了一个相当简单的SQL子查询。

  • 这时,我们发现您的NON-sql NON-platform无法识别=用于分配计算或实数的列名。我已将语法更改为使用AS(均为标准SQL语法)。

  • 如果这不起作用,则意味着该免费软件不是基于SQL的投诉,这甚至在SQL Basic中也没有。

  

如果我使用module_codemodule_year作为主 composite

,此查询是否可行?
  • 它与外部查询或子查询没有任何区别,因为它没有被引用(Module_Years的所有Module_Codes,全部Student_ID的平均值)。只是列名已更改。

  • 它确实对数据模型和理解有所不同。如果该PK是Module的正确PK,则可以。正确的IFF是给定的ModuleCode可以容纳多个Module_Year中。

  • 我们未修正数据以符合要求。我们修正了数据以符合 Reality 。然后所需的代码非常简单。此外,这消除了对数据结构的持续更改,因为 Reality的结构不会更改(内容会更改)。

  

将course_current_year更改为course_current_status使其更有意义。

     

我想做的是提取2017年即将毕业的学生的数据,即Course_Current_Status ='Graduated-2017'。

  • 那没有道理。您当然不希望对查询中的= "2017"进行硬编码,而需要当前年份。因此,请从postGresNONsql具有的用于获取当前日期的任何功能中获取该信息,并仅获取年份。

  • 似乎您需要学生的毕业年份。不是“当前”。 在查询中,您需要当年要毕业的学生。我已经相应地更改了数据模型和代码。

注意

为了弄清楚您的问题,我不得不格式化您的文本,并建立一个数据模型。如果您有兴趣:Hadi Data Model。数据模型会根据更改和注释进行更新。

顺便说一句,表中的命名比大多数命名更好,但仍然有些疯狂。

答案 1 :(得分:0)

由于您没有指定确切的数据库,因此很难使用条件表达式来求和。例如,MySQL具有if函数,SQL Server具有select / case / when。取决于数据库引擎,语法可能会完全不同。

如果我们使用子查询的语法,我们可以这样:

with student_year_grade(Student_ID,avg_marks,Course_Current_Year)
as (
SELECT 
    Students.Student_ID,
    AVG(Enrollment.Marks_obtained) AS avg_marks ,
    Course_Current_Year
FROM
    Students LEFT JOIN
    Enrollment ON Students.Student_ID=Enrollment.Student_ID 
WHERE 
    Enrollment.Program_Year_When_Enrolled = 'Third' 
GROUP BY 
    Students.Student_ID, Course_Current_Year
)
select Student_ID, 
   sum( if(Course_Current_Year='G',avg_marks,0) as current_mark,
   sum( if(Course_Current_Year='-1',avg_marks,0) as prev_mark,
   sum( if(Course_Current_Year='-2',avg_marks,0) as prev_mark

group by student ID 
from student_year_grade

您将必须用上一年的代码替换-1,并用前一年的代码替换-2。并可能乘以您的1/3或2/3因子

答案 2 :(得分:0)

您希望使用学生的汇总入学值来加入他们。与2017年一样,在这里我们不需要外部联接。每个学生都将在两年内获得分数,否则-他们如何毕业?至少我是这样理解的。

除法时要注意的一件事是PostgreSQL应用了所谓的整数除法,其中小数被吞噬,例如3/2 =1。例如,一种避免将此乘以1.0的方法。

select
  s.student_id, 
  e.average_second_year_marks, 
  e.average_third_year_marks, 
  e.overall_marks
from students s
join
(
  select student_id,
    avg(case when program_year_when_enrolled = 'Second' then marks_obtained end)
      as average_second_year_marks,
    avg(case when program_year_when_enrolled = 'Third' then marks_obtained end)
      as average_third_year_marks,
    (
     (sum(case when program_year_when_enrolled = 'Second' then marks_obtained end) * 1.0) +
     (sum(case when program_year_when_enrolled = 'Third' then marks_obtained end) * 2.0)
    ) / 3.0 as overall_marks
  from enrollment
  group by student_id
) e on e.student_id = s.student_id
where s.course_current_status = 'Graduated-2017';