LEFT OUTER JOIN ...使用不同的匹配键

时间:2015-01-23 13:19:46

标签: mysql sql

所以......这有点令人困惑。我有2个表,一个基本上是人员和主题的代码和名称列表,然后是一个值,例如:

enter image description here

第二个表只是一个主题列表,带有一个值和一个“结果”,它也只是一个数值:

enter image description here

现在,我要做的是在第一个表上执行LEFT OUTER JOIN,匹配主题和值,以从第二个表中获取“结果”字段。这在大多数情况下都很简单,因为它们几乎总是完全匹配,但是在某些情况下会出现这种情况,在这种情况下问题是表1中的“值”低于所有情况表2中的值。在这种情况下,我想简单地执行JOIN,就好像表1中的值等于表2中该主题的最低值

要突出显示 - 如果我在主题和值上匹配,则LEFT OUTER JOIN将不为第2行返回任何内容,因为表2中没有值为30的地理行。在这种情况下,我希望它只是选择值为35的行,然后在JOIN中返回Result字段。

这有意义吗?而且,有可能吗?

非常感谢。

4 个答案:

答案 0 :(得分:1)

您可以在此处使用Cross Apply。可能有更好的解决方案性能。

declare @people table(
Code int,
Name varchar(30),
Topic varchar(30),
Value int
)

declare @topics table(
[Subject] varchar(30),
Value int,
Result int
)

INSERT INTO @people values (1, 'Doe,John', 'History', 25),
(2, 'Doe,John', 'Geography', 30),
(3, 'Doe,John', 'Mathematics', 45),
(4, 'Doe,John', 'Brad Pitt Studies', 100)

INSERT INTO @topics values ('History', 25, 95),
('History', 30, 84),
('History', 35, 75),
('Geography', 35, 51),
('Geography', 40, 84),
('Geography', 45, 65),
('Mathematics', 45, 32),
('Mathematics', 50, 38),
('Mathematics', 55, 15),
('Brad Pitt Studies', 100, 92),
('Brad Pitt Studies', 90, 90)

SELECT p.Code, p.Name,
case when p.Value < mTopic.minValue THEN mTopic.minValue
    else p.Value
END, mTopic.minValue
FROM @people p
CROSS APPLY 
(
SELECT [Subject], 
    MIN(value) as minValue
FROM @topics t
WHERE p.Topic = t.Subject
GROUP BY [Subject]
) mTopic

我也假设:

在大多数情况下这很简单,因为它们几乎总是完全匹配,但是在某些情况下会出现这种情况,在这种情况下,问题将是“价值”在表1低于表2中的所有值。

是对的。如果有时间值不等于任何主题值并且不小于最小值,则它将返回people.value,即使它不是“有效”值(假设主题是有效值列表,但我不能从你的描述中说出来。)

从技术上讲,你只需要在select语句中使用case语句,而不是下面的mTopic.minValue,但我认为这个例子显示效果更好。

答案 1 :(得分:0)

在这种情况下,我会做两个连接而不是一个连接。像这样:

select * 
from Table1 T1 
LEFT JOIN Table2 T2 on T1.Topic=T2.subject and T1.Value=T2.VALUE 
LEFT JOIN Table2 as T3 on T1.Topic=T3.Subject and T1.Value<T2.Value

选择要从中获取值的表格。如果T2.value为null,则使用T3.Value ELSE T2.Value。希望这可以帮助你

答案 2 :(得分:0)

执行此操作的另一种方法是使用临时表来保存不同的值。 首先插入完全匹配,然后插入初始选择中未找到的非精确匹配,最后从临时表中获取所有结果。这个解决方案比其他解决方案更多,所以只需添加它作为替代方案。

示例(SqlFiddle):

架构优先

create table students
     ( code     integer,
       name     varchar(50),
       topic    varchar(50),
       value    integer );

create table subjects
     ( subject  varchar(50),
       value    varchar(50),
       result   integer );

insert students
     ( code, name, topic, value )
values
     ( 1, 'Doe, John', 'History',           25),
     ( 2, 'Doe, John', 'Geography',         30),
     ( 3, 'Doe, Jane', 'Mathematics',       45),
     ( 4, 'Doe, Jane', 'Brad Pitt Studies', 100);

insert subjects
     ( subject, value, result )
values
     ( 'History',           25,  95 ),
     ( 'History',           30,  84 ),
     ( 'History',           35,  75 ),
     ( 'Geography',         35,  51 ),
     ( 'Geography',         40,  84 ),
     ( 'Geography',         45,  65 ),
     ( 'Mathematics',       45,  32 ),
     ( 'Mathematics',       50,  38 ),
     ( 'Mathematics',       55,  15 ),
     ( 'Brad Pitt Studies', 100, 92 ),
     ( 'Brad Pitt Studies', 90,  90 );

实际的SQL查询:

-- Temp table to hold our results
create temporary table tempresult
     ( code         integer,
       name         varchar(50),
       topic        varchar(50),
       studentvalue integer,
       subjectvalue integer,
       result       integer );

-- Get the exact results
insert tempresult
     ( code,
       name,
       topic,
       studentvalue,
       subjectvalue,
       result )
select stu.code,
       stu.name,
       stu.topic,
       stu.value as 'student_value',
       sub.value as 'subject_value',
       sub.result
  from students stu
         join
       subjects sub on sub.subject = stu.topic
                   and sub.value   = stu.value;

-- Get the non-exact results, excluding the 'students' that we already
-- got in the  first insert
insert tempresult
     ( code,
       name,
       topic,
       studentvalue,
       subjectvalue,
       result )    
select stu.code,
       stu.name,
       stu.topic,
       stu.value as 'student_value',
       sub.value as 'subject_value',
       sub.result
  from students stu
         join
       subjects sub on sub.subject = stu.topic
                       -- Business logic here: Take lowest subject value that is just above the student's value
                   and sub.value   = (select min(sub2.value)
                                        from subjects sub2
                                       where sub2.subject = stu.topic
                                         and sub2.value   > stu.value)
 where not exists (select 1
                     from tempresult tmp
                    where tmp.code  = stu.code
                      and tmp.name  = stu.name
                      and tmp.topic = stu.topic)

-- Get our resultset
select code,
       name,
       topic,
       studentvalue,
       subjectvalue,
       result
  from tempresult
 order by code,
          name,
          topic,
          studentvalue,
          subjectvalue,
          result

答案 3 :(得分:0)

要求中未调用左连接。你想在T1.Subject = T2.Topic时加入,然后当T1.Value = T2.Value或T1.Value&lt;时加入。 T2.Value和T2.Value是最小值。就这样写出来:

select  p.*, t.Result
from    @People p
join    @Topics t
    on  t.Subject   = p.Topic
    and(    t.Value     = p.Value
        or(     p.Value < t.value 
            and t.Value =(
            select  Min( Value )
            from    @Topics
            where   Subject = t.Subject )));

生成:

Code Name     Topic             Value Result
---- -------- ----------------- ----- ------
1    Doe,John History           25    95
2    Doe,John Geography         30    51
3    Doe,John Mathematics       45    32
4    Doe,John Brad Pitt Studies 100   92