SQL Server Trouble with T-SQL joins on multiple rows

sczxawaw  于 2023-06-21  发布在  其他
关注(0)|答案(1)|浏览(121)

I have the following code

DECLARE @instance TABLE
                  (
                      instance_name VARCHAR(40), 
                      b VARCHAR(40), 
                      c VARCHAR(40)
                  )

INSERT INTO @instance
VALUES 
('inst1', 'a', 'a'),
('inst2', 'a', 'b'),
('inst3', 'a', 'c')

DECLARE @db TABLE
            (
                [database_name] VARCHAR(40), 
                instance_name VARCHAR(40), 
                f VARCHAR(40)
            )

INSERT INTO @db
VALUES 
('db1', 'inst1', 'a'),
('db2', 'inst1', 'b')

DECLARE @override TABLE
                  (
                      name VARCHAR(40), 
                      instance_name VARCHAR(40), 
                      [database_name] VARCHAR(40), 
                      i VARCHAR(40)
                  )

INSERT INTO @override
VALUES 
('inst1.db1', 'inst1', 'db1', 'a'),
('inst1', 'inst1', NULL, 'b')

SELECT
    i.instance_name, d.database_name, s.i  
FROM
    @instance i
JOIN
    @db d ON i.instance_name = d.instance_name
FULL OUTER JOIN 
    @override s ON d.instance_name + '.' + d.database_name = s.name 
                   OR (d.instance_name = s.name AND s.database_name IS NULL)

However the output results in

Instance_namedatabase_nameValue
inst1db1a
inst1db1b
inst1db2b

The value of inst1.db1 should be a but the results show both a and b.

I believe this to be a join issue.

Can anyone please provide guidance?

1bqhqjot

1bqhqjot1#

If you want to be able to define overrides for both instance+database and instance-only, but want an instance+database match to supersede an instance-only match, you will need additional logic.

Two options come to mind:

  1. Define two joins and use COALESCE() to select the preferred match.
  2. Use an OUTER APPLY(SELECT TOP 1...ORDER BY) to pick the match of higher priority.

Something like:

select i.instance_name, d.database_name, coalesce(s1.i, s2.i) as Value
from @instance i
join @db d on i.instance_name = d.instance_name
left join @override s1 on
    d.instance_name + '.' + d.database_name = s1.name
left join @override s2 on
    d.instance_name = s2.name and s2.database_name IS NULL

or

select i.instance_name, d.database_name, s.i as Value
from @instance i
join @db d on i.instance_name = d.instance_name
outer apply (
    select top 1 s.*
    from @override s
    where d.instance_name + '.' + d.database_name = s.name 
        or (d.instance_name = s.name AND s.database_name IS NULL)
    order by case when s.database_name is not null then 1 else 2 end
) s

(Formatting in style of original post, before it was mass-reformatted.)

Results:
| instance_name | database_name | Value |
| ------------ | ------------ | ------------ |
| inst1 | db1 | a |
| inst1 | db2 | b |

The second unwanted inst1.db1 result has been eliminated. See this db<>fiddle for a working demo.

相关问题