I have two tables. One holds Objects and the other holds Settings about each object. Not all of the rows in the Objects table have a corresponding row in the Settings table. There is a special row in the Settings table that is supposed to be used for the "Other" objects.
How can I create a join between Objects and Settings such that I get the given setting if there is one or the "Other" setting if there isn't?
For example consider the following script:
CREATE TABLE #Objects (Code nvarchar(20) not null);
CREATE TABLE #Settings (Code nvarchar(20) not null, Value int not null);
INSERT INTO #Objects
VALUES
('A'),
('B'),
('D')
INSERT INTO #Settings
VALUES
('A', 1),
('B', 2),
('C', 3),
('Other', 4)
SELECT
#Objects.Code,
#Settings.Value
FROM
#Objects
JOIN #Settings
ON #Objects.Code = #Settings.Code
OR #Settings.Code = 'Other'
DROP TABLE #Settings, #Objects
I'm wanting to get this:
Code | Value
---- | -----
A | 1
B | 2
D | 4
What I'm actually getting is:
Code | Value
----- | -----
A | 1
A | 4
B | 2
B | 4
D | 4
You can do this with an APPLY:
SELECT o.Code, s.Value
FROM #Objects o
CROSS APPLY (
SELECT TOP 1 *
FROM #Settings s
WHERE s.Code = o.Code or s.Code = 'Other'
ORDER BY case when s.Code = o.Code then 0 else 1 end
) s
For fun: a hybrid from answers by Gurv, jyao and SqlZim, which are all variations on the same basic theme:
SELECT o.Code, s2.Value
FROM #Objects o
LEFT JOIN #Settings s1 on s1.Code = o.Code
INNER JOIN #Settings s2 on s2.Code = coalesce(s1.Code, 'Other')
So far, this approach (LEFT JOIN + the INNER JOIN ON COALESCE() ) is my favorite option.
Note that this only works if there can be only one Settings record per Object record. If that ever changes, the APPLY answer still works, but other answers here might not work.
Another way is to use CTE to add an additional column [Alternative_code] for [#Object] table that has value "Other" for [Code] not existing in [#Settings]
and then using this CTE to join with #Settings table as shown below
; with c as (
select alternative_Code = isnull(s.code, 'Other'), o.Code
from #Objects o
left join #Settings s
on o.Code = s.Code)
select c.Code, s.value
from c
inner join #Settings s
on c.alternative_Code = s.Code
Using a left join to get null where o.Code has no match in #Settings
, and using coalesce() to return the designated replacement value
from #Settings when s.Value is null.
You could use isnull() instead of coalesce, the result would be the same in this instance.
I am not sure if this acceptable, but it returns the correct results:
select
o.Code
, coalesce(s.Value,x.Value) as Value
from #Objects o
left join #Settings s
on o.Code = s.Code
cross join (
select top 1 value
from #Settings
where Code = 'Other'
) x
rextester demo: http://rextester.com/EBUG86037
returns:
+------+-------+
| Code | Value |
+------+-------+
| A | 1 |
| B | 2 |
| D | 4 |
+------+-------+
In the form #RBarryYoung prefers:
select
o.Code
, coalesce(s.Value,x.Value) as Value
from #Objects o
left join #Settings s
on o.Code = s.Code
inner join #Settings x
on x.Code = 'Other'
This is more concise (saves you many keystrokes) and generates the same execution plan as my initial answer. Whether it is more or less clear about what it is doing is up to you, I like both.
If there is going to be one "Other" value then you can just do the join twice - a left join and another one which is effectively a cross join:
select o.Code,
coalesce(s.Value, s2.value) as value
from #Objects o
left join #Settings s on o.Code = s.Code
join #Settings s2 on s2.Code = 'Other'
Related
I have these tables:
names
id | name
7 | 'a'
8 | 'b'
9 | 'c'
group_names
id | group_of_names
1 | '7'
2 | '9,8'
3 | '7,8,9'
how to build a select that returns their names instead, separated by semicolon, preserving the original order as in:
id | single_text
1 | 'a'
2 | 'c;b'
3 | 'a;b;c'
I managed to do
select g.id, string_to_array(g.group_of_names,',')::int[] from group_names g;
id | string_to_array
1 | {7}
2 | {9,8}
3 | {7,8,9}
but i don't know how to, returning several arrays, for each of them, concatenate texts based on their ids
If the order of resulting strings is irrelevant:
select g.id, string_agg(n.name, ';')
from group_names g
join names n
on n.id = any(string_to_array(g.group_of_names, ',')::int[])
group by g.id
otherwise:
select g.id, string_agg(n.name, ';' order by ord)
from names n
join (
select id, elem, ord
from group_names
cross join regexp_split_to_table(group_of_names, ',')
with ordinality as arr(elem, ord)
) g
on n.id = g.elem::int
group by g.id
Test it in db<>fiddle.
In Postgres 14+ you can use string_to_table() instead of regexp_split_to_table().
You can try this way: Using the ANY operator to check if n.id value is in the array of group_of_names.
SELECT gn.id, string_agg(n.name, ';') AS single_text
FROM names n
INNER JOIN group_names gn ON n.id::text = ANY(string_to_array(gn.group_of_names, ','))
GROUP BY gn.id
ORDER BY gn.id;
Or this way: using your query and unnest() function to expand an array to a set of rows.
SELECT gn.id, string_agg(n.name, ';')
FROM names n
INNER JOIN (SELECT g.id, unnest(string_to_array(g.group_of_names, ',')::int[]) AS name_id
FROM group_names g) AS gn ON n.id = gn.name_id
GROUP BY gn.id
ORDER BY gn.id;
SELECT g.id, string_agg(n.name,';') AS single_text
FROM group_names AS g
CROSS JOIN LATERAL regexp_split_to_table (g.group_of_names, ',') AS gn(element)
INNER JOIN names AS n
ON n.id :: text = gn.element
GROUP BY g.id
T1 is a table of company and their (multiple users), T2 is table of registered users. I counted, for each company in T1, how many of their users are in T2 but need c3 to appear in the result table with #regUser == 0:
T1:
company user
c1 u1
c1 u2
c2 u2
c2 u3
c3 u4
c3 u1
T2:
user
u2
u3
So the resultant table should look like:
company #regUser
c1 1
c2 2
c3 0
With the following code I'm only getting the results for non-null companies:
select t1s.company, count(1)
from (select * from t1) t1s
cross apply (select *
from t2 t2s
where t2s.reguser = t1s.[user]) t12s
group by t1s.company
Thanks
just use left join
select t1.company,count(t2.user)
from t1 left join t2 on t1.user=t2.user
group by t1.company
the subquery is not necessary according to your requirement
but if you want to use apply then you need like below query
select t1s.company, count(t12s.Users)
from (select * from t1) t1s
outer apply (select Users
from t2 t2s
where t2s.Users = t1s.[Users]) t12s
group by t1s.company
output
company #regUser
c1 1
c2 2
c3 0
demolink
You can use a LEFT JOIN to get all the information of the left table with matching information from right table. By using a GROUP BY you can group the rows by company and get the COUNT of registered users for each company:
SELECT t1.company, COUNT(t2.[user]) AS regUser
FROM t1 LEFT JOIN t2 ON t1.[user] = t2.[user]
GROUP BY t1.company
ORDER BY t1.company ASC
You can also use the CROSS APPLY to solve this:
SELECT t1.company, SUM(CASE WHEN t1.[user] = t2.[user] THEN 1 ELSE 0 END) AS regUser
FROM t1 CROSS APPLY t2
GROUP BY t1.company
ORDER BY t1.company ASC
demo on dbfiddle.uk
All you need is a left join :
select company,count(t2.[user])
from t1 left outer join t2 on t1.[user]=t2.[user]
group by company
The question's query is overcomplicated. For example, from (select * from t1) t1s is equivalent to from t1 as t1s.
Just a LEFT JOIN with SUM()
CREATE TABLE T1(
Company VARCHAR(20),
Users VARCHAR(20)
);
CREATE TABLE T2(
Users VARCHAR(20)
);
INSERT INTO T1 VALUES
('c1', 'u1'),
('c1', 'u2'),
('c2', 'u2'),
('c2', 'u3'),
('c3', 'u4'),
('c3', 'u1');
INSERT INTO T2 VALUES
('u2'),
('u3');
SELECT T1.Company,
SUM(CASE WHEN T2.Users IS NULL THEN 0 ELSE 1 END) Cnt
FROM T1 LEFT JOIN T2
ON T1.Users = T2.Users
GROUP BY T1.Company;
Returns:
+---------+-----+
| Company | Cnt |
+---------+-----+
| c1 | 1 |
| c2 | 2 |
| c3 | 0 |
+---------+-----+
Live Demo
I have a select like this:
SELECT
FORMAT(AVG([DC].[ContractedAmount]) , '$###,###,###,###.##') AS [AverageContractedAmount]
FROM
[DesignCustomer] AS [DC]
INNER JOIN
[Design] AS [D] ON [DC].[DesignKey] = [D].[DesignKey]
INNER JOIN
[Task] AS [T] ON [D].[DesignKey] = [t].[DesignKey]
INNER JOIN
[ProjectDesign] AS [PD] ON [D].[DesignKey] = [PD].[DesignKey]
INNER JOIN
[Project] AS [P] ON [PD].[ProjectKey] = [P].[ProjectKey]
INNER JOIN
[Address] AS [A] ON [A].[AddressGuid] = [P].[ProjectGuid]
As you can see I get the Average of Contracted Amount. I get something like this:
+---------------------------+
| [AverageContractedAmount] |
+---------------------------+
| $1,000.00 |
+---------------------------+
Now I want to get project who is more closest to that value
For example if I have 5 projects in project table like:
+----------------+
| ProjectName |
+----------------+
| First Project |
| Second Project |
| Third Project |
| Four Project |
| Five Project |
+----------------+
Relation of [DC] with project is something like this:
+----------------+------------------+
| ProjectName | ContractedAmount |
+----------------+------------------+
| First Project | 500 |
| Second Project | 700 |
| Third Project | 300 |
| Four Project | 950 |
| Five Project | 800 |
+----------------+------------------+
I want the query to return the Four Project Name because its ContractedAmount value is the closest to the AVG value. How can I achieve this? Regards
Without putting much thought into this, you can just dump that into a subquery and subtract, sort by the difference, and keep the top result:
SELECT TOP 1
project_name,
FROM Project
ORDER BY Abs(ContractedAmount -
(
SELECT
AVG([DC].[ContractedAmount]) AS [AverageContractedAmount]
FROM [DesignCustomer] AS [DC]
INNER JOIN [Design] AS [D] ON [DC].[DesignKey] = [D].[DesignKey]
INNER JOIN [Task] AS [T] ON [D].[DesignKey] = [t].[DesignKey]
INNER JOIN [ProjectDesign] AS [PD] ON [D].[DesignKey] = [PD].[DesignKey]
INNER JOIN [Project] AS [P] ON [PD].[ProjectKey] = [P].[ProjectKey]
INNER JOIN [Address] AS [A] ON [A].[AddressGuid] = [P].[ProjectGuid]
)) ASC
It seems that you've already figured out getting your average, so taking your sample tables, but fudged to get an average of $1000, I wrote a single select statement using the average to get your answer. For this answer, I've just calculated the average from the table rather than your code in order to get the variable #AVERAGE.
CREATE TABLE #Amts (Project VARCHAR(20), Amount INT);
INSERT INTO #Amts
VALUES
('One Project', 500),
('Two Project', 1500),
('Three Project', 300),
('Four Project', 1700),
('Five Project', 1100),
('Six Project', 900) ;
DECLARE #AVERAGE INT = (SELECT AVG(Amount) FROM #Amts) -- $1000
SELECT TOP 1 Project -- Since you said whichever project is suitable, this should be fine.
FROM #Amts AS A
WHERE ABS(A.Amount - #AVERAGE) = (SELECT MIN(ABS(Amin.Amount - #AVERAGE)) FROM #Amts AS Amin)
ORDER BY Project
DROP TABLE #Amts
This gives you the answer of "Five Project".
using DENSE_RANK:
WITH getavg AS (
SELECT AVG([DC].[ContractedAmount]) OVER() AS [AverageContractedAmount]
, p.projectname, p.contractedamount
FROM [DesignCustomer] AS [DC]
INNER JOIN [Design] AS [D] ON [DC].[DesignKey] = [D].[DesignKey]
INNER JOIN [Task] AS [T] ON [D].[DesignKey] = [t].[DesignKey]
INNER JOIN [ProjectDesign] AS [PD] ON [D].[DesignKey] = [PD].[DesignKey]
INNER JOIN [Project] AS [P] ON [PD].[ProjectKey] = [P].[ProjectKey]
INNER JOIN [Address] AS [A] ON [A].[AddressGuid] = [P].[ProjectGuid]
),
ranked as (
SELECT projectname, contractedamount
DENSE_RANK() OVER(ORDER BY ABS([AverageContractedAmount]-contractedamount)) AS dr
FROM getavg)
SELECT * FROM ranked
WHERE dr <= 5
Strictly closest would be:
; WITH AvgAmt AS (
SELECT AVG([DC].[ContractedAmount]) AS [AverageContractedAmount]
FROM [DesignCustomer] AS [DC]
INNER JOIN [Design] AS [D] ON [DC].[DesignKey] = [D].[DesignKey]
INNER JOIN [Task] AS [T] ON [D].[DesignKey] = [t].[DesignKey]
INNER JOIN [ProjectDesign] AS [PD] ON [D].[DesignKey] = [PD].[DesignKey]
INNER JOIN [Project] AS [P] ON [PD].[ProjectKey] = [P].[ProjectKey]
INNER JOIN [Address] AS [A] ON [A].[AddressGuid] = [P].[ProjectGuid]
)
SELECT TOP(1) P.ProjectName, DC.ContractedAmount
FROM [DesignCustomer] AS [DC]
INNER JOIN [Design] AS [D] ON [DC].[DesignKey] = [D].[DesignKey]
INNER JOIN [ProjectDesign] AS [PD] ON [D].[DesignKey] = [PD].[DesignKey]
INNER JOIN [Project] AS [P] ON [PD].[ProjectKey] = [P].[ProjectKey]
ORDER BY ABS(AvgAmt.[AverageContractedAmount] - ContractedAmount);
Depending on your data, you might want to exclude a few INNER JOIN's from the CTE itself simply to improve performance if the data doesn't require it.
The solution can also be easily parametrized to select closest #n contracted amounts instead of the closest one (converting SELECT TOP(1) to SELECT TOP(#n)).
As an addition, if there are multiple projects with the same difference to the average contracted amount, you might want to add some other columns into ORDER BY to break the tie. This is something you can decide on as the one with the knowledge of what data represents and what you expect from it.
I am looking at modifying the following query in a program (this was written before my time here as Software Engineer so please bear with me...):
SELECT
Participant.ParticipantID AS "ParticipantId",
Stream.StreamName AS "StreamName",
ParticipantStatistics.ConnectTime AS "ConnectTime",
ParticipantStatistics.DisconnectTime AS "DisconnectTime",
FormField.FieldLabel AS "FieldLabel",
RegistrantAnswer.Answer AS "Answer"
FROM ParticipantStatistics
INNER JOIN Participant ON ParticipantStatistics.ParticipantId = Participant.ParticipantID
INNER JOIN Registrant ON Registrant.RegistrantId = Participant.RegistrantID
LEFT OUTER JOIN RegistrantAnswer ON RegistrantAnswer.RegistrantID = Registrant.RegistrantID
INNER JOIN Event ON Event.EventId = Participant.EventID
INNER JOIN Stream ON Stream.MediaEventId = Event.EventId
LEFT OUTER JOIN FormField ON RegistrantAnswer.FormFieldId = FormField.FormFieldId
LEFT OUTER JOIN (SELECT DISTINCT ParticipantID, SurveyID FROM ParticipantSurvey)
AS SurveyCompleted ON SurveyCompleted.ParticipantID = Participant.ParticipantID
WHERE Stream.StreamId = '2235'
AND Participant.Visible = 1
ORDER BY Participant.ParticipantID, OrderNumber, ParticipantStatistics.ConnectTime ASC
This query gives me the following information:
What I would like to do is modify the above query to return the results as one row as follows:
| 315314 | ffbc110729 | 2011-10-27 03:13:06.240 | 2011-10-27 03:17:12.473 | **First Name, Last Name, Email, Company** | **ads, asd, asd#asd.com, asdf** |
Where the last two columns are combined, separated by commas in a single row.
Is this possible using either STUFF or CONCAT? I am new to T-SQL so please let me know if you need further clarification.
Best Regards,
EDIT: When I try to edit with STUFF FOR XML PATH I set it up the following way:
SELECT
Participant.ParticipantID AS "ParticipantId",
Stream.StreamName AS "StreamName",
ParticipantStatistics.ConnectTime AS "ConnectTime",
ParticipantStatistics.DisconnectTime AS "DisconnectTime",
STUFF ((SELECT ','+ FormField.FieldLabel FROM FormField WHERE FormField.FormFieldID = RegistrantAnswer.FormFieldID FOR XML PATH ('')),1,1,'')
FROM ParticipantStatistics
INNER JOIN Participant ON ParticipantStatistics.ParticipantId = Participant.ParticipantID
INNER JOIN Registrant ON Registrant.RegistrantId = Participant.RegistrantID
LEFT OUTER JOIN RegistrantAnswer ON RegistrantAnswer.RegistrantID = Registrant.RegistrantID
INNER JOIN Event ON Event.EventId = Participant.EventID
INNER JOIN Stream ON Stream.MediaEventId = Event.EventId
LEFT OUTER JOIN FormField ON RegistrantAnswer.FormFieldId = FormField.FormFieldId
LEFT OUTER JOIN (SELECT DISTINCT ParticipantID, SurveyID FROM ParticipantSurvey)
AS SurveyCompleted ON SurveyCompleted.ParticipantID = Participant.ParticipantID
WHERE Stream.StreamId = '2235'
AND Participant.Visible = 1
ORDER BY Participant.ParticipantID, OrderNumber, ParticipantStatistics.ConnectTime ASC
I then receive the following:
Again, I am fairly new to T-SQL so maybe I am setting it up wrong? Any help is greatly appreciated.
Try something like this:
SELECT
Participant.ParticipantID AS "ParticipantId",
Stream.StreamName AS "StreamName",
ParticipantStatistics.ConnectTime AS "ConnectTime",
ParticipantStatistics.DisconnectTime AS "DisconnectTime",
STUFF(
(SELECT ', ' + FormField.FieldLabel as'text()'
FROM Registrant
LEFT OUTER JOIN RegistrantAnswer ON RegistrantAnswer.RegistrantID = Registrant.RegistrantID
LEFT OUTER JOIN FormField ON RegistrantAnswer.FormFieldId = FormField.FormFieldId
WHERE Registrant.RegistrantId = Participant.RegistrantID
FOR XML PATH('')
), 1, 2, '') AS "FieldLabel",
STUFF(
(SELECT ', ' + RegistrantAnswer.Answer as'text()'
FROM Registrant
LEFT OUTER JOIN RegistrantAnswer ON RegistrantAnswer.RegistrantID = Registrant.RegistrantID
WHERE Registrant.RegistrantId = Participant.RegistrantID
FOR XML PATH('')
), 1, 2, '') AS "Answer"
FROM ParticipantStatistics
INNER JOIN Participant ON ParticipantStatistics.ParticipantId = Participant.ParticipantID
INNER JOIN Registrant ON Registrant.RegistrantId = Participant.RegistrantID
INNER JOIN Event ON Event.EventId = Participant.EventID
INNER JOIN Stream ON Stream.MediaEventId = Event.EventId
WHERE Stream.StreamId = '2235'
AND Participant.Visible = 1
ORDER BY Participant.ParticipantID, OrderNumber, ParticipantStatistics.ConnectTime ASC
Using STUFF + FOR XML PATH('') is usually the most practical in SQL Server to concatenate strings. This sample first populates the resultset in an intermediary temporary table for the sake of keeping things readable and manageable.
SELECT *
INTO #fiddle_table
FROM (
VALUES
(315314,'ffbc110729',{ts '2011-10-27 03:13:06.240'},{ts '2011-10-27 03:17:12.473'},'First Name','ads'),
(315314,'ffbc110729',{ts '2011-10-27 03:13:06.240'},{ts '2011-10-27 03:17:12.473'},'Last Name','asd'),
(315314,'ffbc110729',{ts '2011-10-27 03:13:06.240'},{ts '2011-10-27 03:17:12.473'},'Email','asd#asd.com'),
(315314,'ffbc110729',{ts '2011-10-27 03:13:06.240'},{ts '2011-10-27 03:17:12.473'},'Company','asdf')
) AS v(participantid,streamname,connecttime,disconnecttime,fieldlabel,answer);
;WITH cte AS (
SELECT DISTINCT
participantid,streamname,connecttime,disconnecttime
FROM
#fiddle_table
)
SELECT
participantid,streamname,connecttime,disconnecttime,
fieldlabels=STUFF((
SELECT ', '+fieldlabel
FROM #fiddle_table AS i
WHERE i.participantid=o.participantid
FOR XML PATH('')
),1,2,''
),
answers=STUFF((
SELECT ', '+answer
FROM #fiddle_table AS i
WHERE i.participantid=o.participantid
FOR XML PATH('')
),1,2,''
)
FROM cte AS o;
DROP TABLE #fiddle_table;
Result:
+---------------+------------+-------------------------+-------------------------+---------------------------------------+-----------------------------+
| participantid | streamname | connecttime | disconnecttime | fieldlabels | answers |
+---------------+------------+-------------------------+-------------------------+---------------------------------------+-----------------------------+
| 315314 | ffbc110729 | 2011-10-27 03:13:06.240 | 2011-10-27 03:17:12.473 | First Name, Last Name, Email, Company | ads, asd, asd#asd.com, asdf |
+---------------+------------+-------------------------+-------------------------+---------------------------------------+-----------------------------+
I have two tables -
Table Records with columns ID, Code,ProviderId
Table Codes with columns Code, Number, ProviderId
Sample Data:
Table Records
ID Code ProviderId
1 ABC 1
2 DEF 2
3 XYZ 1
4 PQR 2
Table Codes
Code Number ProviderId
ABC 1111 1
Default 9999 1
XYZ 2222 2
Default 4444 2
All the rows in Records table will have a code. Codes table will have a set of codes defined with other information. It is not necessary that all the codes in Records table will have an entry in Codes table. For Code in Records table if corresponding value exists then select the same else need to select Default code based on the ProviderID column.
My expected result is:
ID Code Number
-------------------------
1 ABC 1111
2 DEF 9999 -> Picked up the default for ProviderId 1
3 XYZ 2222
4 PQR 4444 -. Picked up the default for ProviderId 2
I was able to achieve this using left outer join to add the records into a table variable and then again performing an inner join.
I was wondering if I can achieve this in a single select.
You can do this with left joins:
select r.id, r.code, coalesce(c.number, cd.number) as number:
from records r left join
codes c
on r.code = c.code left join
codes cd
on r.providerid = c.providerid and c.code = 'Default';
That is, lookup both values. Choose the one based on the code if there is a match; otherwise, use the default.
I think, your 'DEF' or 'PQR' entry is wrong, eigher one should have "Providerid" is 1. I prepare a sample from your data.
Use some code of #Gordon. but logic is different.
declare #records table(id int, code varchar(50), providerid int)
declare #code table(code varchar(50), number int, providerid int)
insert into #records values (1,'ABC',1), (2,'DEF',2),(3,'XYZ',1),(4,'PQR',2)
insert into #code values ('ABC',1111,1 ), ('DEFAULT',9999, 1) , ('XYZ',2222,2) , ('Default', 4444, 2)
--your data in wrongly entered , that why the 'DEF' show the wrong thing below.
select
id, r.code ,
case
when ISNULL(c.code , '') = ''
then c1.number
else c.number
end
from
#records r
left outer join #code c on r.code = c.code
left outer join #code c1 on r.providerid = c1.providerid and LOWER( c1.code) = 'default'
--if the default entry is multiple times, then good to use sub-query with top
select
id, r.code ,
case
when ISNULL(c.code , '') = ''
then ( select top 1 number from #code c1 where r.providerid = c1.providerid and LOWER( c1.code) = 'default' )
else c.number
end
from
#records r
left outer join #code c on r.code = c.code
Try this.. As mentioned by others 'PQR' entry is wrong in your OUTPUT. It should not be '9999'. It should be '4444'
SELECT a.ID,b.Code,b.Number
FROM Records a
JOIN code b
ON a.Code = b.Code
UNION ALL
SELECT a.ID,a.Code,b.Number
FROM Records a
JOIN code b
ON a.ProviderId = b.ProviderId
WHERE NOT EXISTS(SELECT 1
FROM Code aa
WHERE aa.Code = a.Code)
AND b.Code = 'default'
SELECT A.ID,A.Code,B.Number,A.ProviderId
FROM #Source A
JOIN #Code B ON A.Code = B.Code
UNION
(SELECT A.ID,A.Code,B.Number,A.ProviderId
FROM #Source A
JOIN #Code B ON A.Code != B.Code
AND B.Code = 'Default'
WHERE A.Code NOT IN (SELECT A.Code
FROM #Source A
JOIN #Code B ON A.Code = B.Code)
AND A.ProviderId = B.ProviderId )
Try using this