I have this 2 tables
Items
ID Type ClientID
========================
1 0 123
2 0 123
Texts
ItemID Language Text
================================
1 Eng Hi there!
1 Spa Holla!
2 Eng bye!
2 Spa bye in Spanish!
In my final result I wat the SQL to return me this table
ID Type Eng Spa
================================
1 0 Hi there! Holla!
2 0 Bye! bye in Spanish!
I tried to create this statement:
SELECT DISTINCT I.ID ,I.Type,
(SELECT T.Text WHERE D.Language='Eng') AS 'Eng',
(SELECT T.Text WHERE D.Language='Spa') AS 'Spa'
FROM Items I
INNER JOIN Texts T ON I.ID=T.ItemID
but i get this result:
ID Type Eng Spa
================================
1 0 Hi there! NULL
1 0 NULL Holla!
2 0 Bye! NULL
2 0 NULL bye in Spanish!
I don't see why a join is necessary. You can just use conditional aggregation:
select t.itemid,
max(case when t.language = 'Eng' then t.text end) as Eng,
max(case when t.language = 'Spa' then t.text end) as Spa
from texts t
group by t.itemid;
Use nesting to make two joins on same table with a filter (where clause) .
Below i have tested on MySQL
SELECT
i.id, eng_table.text AS eng, spa_table.text AS spa
FROM
i
LEFT OUTER JOIN
(SELECT
ItemID AS ID, Text
FROM
t
WHERE
Language = 'ENG') AS eng_table ON i.id = eng_table.id
LEFT OUTER JOIN
(SELECT
ItemID AS ID, Text
FROM
t
WHERE
Language = 'SPA') AS spa_table ON i.id = spa_table.id
Regards,
Bikxs
A solution using joins would look something like....
Select i.ID
,i.[Type]
,t1.[Text] AS [Eng]
,t2.[Text] AS [Spa]
FROM Items i
INNER JOIN Texts t1 ON i.ID = t1.ItemID AND t1.[Language] = 'Eng'
INNER JOIN Texts t2 ON i.ID = t2.ItemID AND t2.[Language] = 'Spa'
WITH Eng
AS
(
SELECT * FROM Texts t WHERE t.Language = 'Eng'
),
Spa
AS
(
SELECT * FROM Texts t WHERE t.Language = 'Spa'
)
SELECT i.ID, i.Type, e.Text AS Eng, s.Text AS Spa
FROM Items i
LEFT JOIN Eng e ON i.ID = e.ItemID
LEFT JOIN Spa s ON i.ID = s.ItemID
Even you can use a dynamic sql query.
Query
DECLARE #query VARCHAR(MAX)
SELECT #query = 'SELECT t1.itemid, MAX(t2.[Type]) AS [Type], ' +
STUFF
(
(
SELECT DISTINCT ',MAX(CASE WHEN t1.[language] = '''+ [language]
+ ''' THEN t1.[Text] END) AS ' + [Language]
FROM Texts
FOR XML PATH('')
),
1,1,'');
SELECT #query += ' FROM texts t1 JOIN items t2 ON t1.ItemId = t2.ID GROUP BY t1.itemid;';
EXECUTE(#query);
Related
i have query below
SELECT #RoleUser = MR.Code FROM MasterRole MR INNER JOIN MasterUsersRole MUR ON MR.Id = MUR.RoleId
INNER JOIN MasterUsers MU ON Mu.UserCode = MUR.UserCode
WHERE MU.UserCode = #UserLoginID
select 1 Num
, MyHistory.ID
, MyHistory.RequestNumber
, MyHistory.FlowID
, MyHistory.FlowProcessStatusID
from
(
select *
from Requests R
inner join
(
--DECLARE #UserLoginID nvarchar(200) = 'dum.testing.3'
select distinct
RequestID
from dbo.RequestTrackingHistory RTH
where IIF(#UserLoginID = 'admin', #UserLoginID, RTH.CreatedBy) = #UserLoginID
OR ( CreatedBy IN
SELECT Mu.UserCode from MasterUsers MU
INNER JOIN MasterUsersRole MUR ON MU.UserCode = MUR.UserCode
INNER JOIN MasterRole MR ON MUR.RoleId = MR.Id
WHERE MR.Code = #RoleUser
)
)
) RT on R.ID = RT.RequestID
) as MyHistory
inner join MasterFlow F on MyHistory.FlowID = F.ID
inner join
(
select FP.ID
, FP.Name
, FP.AssignType
, FP.AssignTo
, FP.IsStart
, case FP.AssignType
when 'GROUP' then
G.Name
end as 'AssignToName'
from MasterFlowProcess FP
left join dbo.MasterRole G on FP.AssignTo = G.ID and FP.AssignType = 'GROUP'
) FP on MyHistory.FlowProcessID = FP.ID
inner join MasterFlowProcessStatus FPS on MyHistory.FlowProcessStatusID = FPS.ID
left join MasterFlowProcessStatusNext FPSN on FPS.ID = FPSN.ProcessStatusFlowID
left join MasterFlowProcess FPN on FPSN.NextProcessFlowID = FPN.ID
left JOIN MasterRole MR ON MR.Id = FPN.AssignTo
left join MasterUsersRole MUR on MR.Id = MUR.RoleId
left join MasterUsers MURO on MUR.UserCode = MURO.UserCode
inner join MasterUsers UC on MyHistory.CreatedBy = UC.UserCode
left join MasterUsers UU on MyHistory.UpdatedBy = UU.UserCode
LEFT JOIN RequestMT RMT ON MyHistory.ID = RMT.RequestID
LEFT JOIN RequestGT RGT ON MyHistory.ID = RGT.RequestID
left join (SELECT sum(QtyCU) countQty , RequestId from dbo.RequestGTDetail where IsActive = 1 group by RequestId) RGTD on RGTD.RequestId = RGT.RequestId
left join (SELECT sum(QtyPCS) countQty , RequestId from dbo.RequestMTDetail where IsActive = 1 group by RequestId) RMTD on RMTD.RequestId = RMT.RequestId
left join (SELECT COUNT(IIF(returnable = 0, returnable, null)) countReturnable , RequestId from dbo.RequestMTDetail group by RequestId) RMTR on RMTR.RequestId = RMT.RequestId
left JOIN dbo.MasterDistributor md ON md.Code = RGT.CustId or md.Code = RMT.CustId
left JOIN dbo.MasterUsersDistributor MUD ON MUD.UserCode = MURO.UserCode AND md.Code = MUD.DistributorCode
LEFT JOIN dbo.MasterReason MRMT ON RMT.ReasonId = MRMT.Id
LEFT JOIN dbo.MasterReason MRGT ON RGT.ReasonId = MRGT.Id
LEFT JOIN dbo.MasterDistributorGroup MDG ON MDG.Id = MD.GroupId
OUTER APPLY dbo.FnGetHistoryApproveDate(MyHistory.Id) AS x
where REPLACE(FPS.Name, '#Requestor', uc.Name) <> 'DRAFT'
AND MUD.DistributorCode IN (SELECT DistributorCode FROM dbo.MasterUsersDistributor WHERE UserCode = #UserLoginID)
i want to add some logic in where clause
this line
==> AND MUD.DistributorCode IN (SELECT DistributorCode FROM dbo.MasterUsersDistributor WHERE UserCode = #UserLoginID)
it depend on the #RoleUser variable, if #RoleUser IN ('A','B') then where clause above is executed, but if #RoleUser Not IN ('A','B') where clause not executed
i,m trying this where clause
AND IIF(#RoleUser IN ('A','B'), MUD.DistributorCode, #RoleUser) IN (SELECT DistributorCode FROM dbo.MasterUsersDistributor WHERE UserCode = IIF(#RoleUser IN ('A','B'), #UserLoginID, NULL))
it didn't work, only executed if #RoleUser IS ('A','B') other than that it return 0 record
any help or advice is really appreciated
thank you
The cleanest way I'm implemented these kind of crazy rules is a
holderTable
and a countVariable against the holder table.
I'll give a generic examples.
This is a "approach" and "philosophy", not a specific answer....with complex WHERE clauses.
DECLARE #customerCountryCount int
DECLARE #customerCountry TABLE ( CountryName varchar(15) )
if ( "the moon is blue on tuesday" ) /* << whatever rules you have */
BEGIN
INSERT INTO #customerCountry SELECT "Honduras" UNION ALL SELECT "Malaysia"
END
if ( "favorite color = green" ) /* << whatever rules you have */
BEGIN
INSERT INTO #customerCountry SELECT "Greenland" UNION ALL SELECT "Peru"
END
SELECT #customerCountryCount = COUNT(*) FROM #customerCountry
Select * from dbo.Customers c
WHERE
(#customerCountryCount = 0)
OR
( exists (select null from #customerCountry innerVariableTable where UPPER(innerVariableTable.CountryName) = UPPER(c.Country) ))
)
This way, instead of putting all the "twisted logic" in an overly complex WHERE statement..... you have "separation of concerns"...
Your inserts into #customerCountry are separated from your use of #customerCountry.
And you have the #customerCountryCount "trick" to distinguish when nothing was used.
You can add a #customerCountryNotExists table as well, and code it to where not exists.
As a side note, you may want to try using a #temp table (instead of a #variabletable (#customerCountry above)... and performance test these 2 options.
There is no "single answer". You have to "try it out".
And many many variables go into #temp table performance (from a sql-server SETUP, not "how you code a stored procedure". That is way outside the scope of this question.
Here is a SOF link to "safe" #temp table usage.
Temporary table in SQL server causing ' There is already an object named' error
I tried to get the first row for a WHERE statement in a recursive table expression. Sadly i'm getting this error:
The TOP or OFFSET operator is not allowed in the recursive part of a recursive common table expression 'cteTree'.
Here's my SQL Query:
WITH cteTree AS(
SELECT
cct.CategoryID AS bla,
ct.CategoryID,ct.ParentCategoryID,
ct.Name,
ct.Published,
CASE
WHEN EXISTS(SELECT ProductID FROM Product WHERE ProductID = (SELECT TOP(1) ProductID FROM ProductCategory WHERE ProductCategory.CategoryID = ct.CategoryID) AND Product.Published = 1) THEN 1
ELSE 0
END AS ProductExists,
1 As Cycle
FROM Category AS ct
LEFT JOIN Category AS cct ON cct.ParentCategoryID = ct.CategoryID
WHERE cct.CategoryID IS NULL
UNION ALL
SELECT
cct.CategoryID AS bla,
ct.CategoryID,
ct.ParentCategoryID,
ct.Name,
ct.Published,
CASE
WHEN cct.ProductExists = 1 THEN 1
WHEN EXISTS(SELECT ProductID FROM Product WHERE ProductID = (SELECT TOP(1) ProductID FROM ProductCategory WHERE ProductCategory.CategoryID = ct.CategoryID) AND Product.Published = 1) THEN 1
ELSE 0
END AS ProductExists,
Cycle + 1
FROM Category AS ct
JOIN cteTree AS cct ON ct.CategoryID = cct.ParentCategoryID
)
SELECT * FROM cteTree
The problem is in the second Case statement under UNION ALL.
SELECT ProductID FROM Product WHERE ProductID = (SELECT TOP(1) ProductID FROM ProductCategory WHERE ProductCategory.CategoryID = ct.CategoryID) AND Product.Published = 1
Does someone know if there's another expression for selecting the first row in a recursive Table expression that works?
There are some tricks to use TOP (1) in a CTE, such as using ROW_NUMBER instead, or putting it into a TVF. But in your case you can just use a normal join:
You should also use NOT EXISTS instead of the LEFT JOIN IS NULL construct.
WITH cteTree AS(
SELECT
cct.CategoryID AS bla,
ct.CategoryID,ct.ParentCategoryID,
ct.Name,
ct.Published,
CASE
WHEN EXISTS (SELECT 1
FROM Product p
JOIN ProductCategory pc ON pc.CategoryID = ct.CategoryID
WHERE p.Published = 1
) THEN 1
ELSE 0
END AS ProductExists,
1 As Cycle
FROM Category AS ct
WHERE NOT EXISTS (SELECT 1
FROM Category AS cct
WHERE cct.ParentCategoryID = ct.CategoryID
)
UNION ALL
SELECT
cct.CategoryID AS bla,
ct.CategoryID,
ct.ParentCategoryID,
ct.Name,
ct.Published,
CASE
WHEN cct.ProductExists = 1 THEN 1
WHEN EXISTS (SELECT 1
FROM Product p
JOIN ProductCategory pc ON pc.CategoryID = ct.CategoryID
WHERE p.Published = 1
) THEN 1
ELSE 0
END AS ProductExists,
Cycle + 1
FROM Category AS ct
JOIN cteTree AS cct ON ct.CategoryID = cct.ParentCategoryID
)
SELECT *
FROM cteTree
How to write query to get data from other table for each columns in existing table.
You can left join three times on system_code:
select
d.id,
s_cat.full_name cat_code_full_name,
s_group.full_name group_code_full_name,
s_other.full_name other_code_full_name
from data_table d
left join system_code s_cat
on s_cat.value = d.cat_code and s.code = 1
left join system_code s_group
on s_group.value = d.group_code and s_group.code = 2
left join system_code s_other
on s_other.value = d.other_code and s_other.code = 3
To avoid repeating the joins, an alternative solution is to do conditional aggregation:
select
d.id,
max(case when s.value = d.cat_code and s.code = 1 then s.full_name end) cat_code_full_name,
max(case when s.value = d.group_code and s.code = 2 then s.full_name end) group_code_full_name,
max(case when s.value = d.other_code and s.code = 3 then s.full_name end) other_code_full_name
from data_table d
left join system_code s on s.value in (d.cat_code, d.group_code, d.other_code)
gtoup by d.id
I guess the problem you are facing here is, how to get full name for all 3 columns. 1 of the method is to join SYSTEM_CODE table thrice -
SELECT DT.ID
,SC1.FULL_NAME CAT_CODE_FULL_NAME
,SC2.FULL_NAME GROUP_CODE_FULL_NAME
,SC3.FULL_NAME OTHER_CODE_FULL_NAME
,DT.PRODUCT
FROM DATA_TABLE DT
JOIN SYSTEM_CODE SC1 ON SC1.VALUE = DT.CAT_CODE
JOIN SYSTEM_CODE SC2 ON SC2.VALUE = DT.CAT_CODE
JOIN SYSTEM_CODE SC3 ON SC3.VALUE = DT.CAT_CODE
I need help creating the below results. I thought of a sql pivot but I don't know how to use it. Looked at a few examples and cannot come up with a solution. Any other ideas on how to accomplish this is also welcome. Status columns must be dynamically generated.
Have three tables, assets, assettypes, assetstatus
Table: assets
assetid int
assettag varchar(25)
assettype int
assetstatus int
Table: assettypes
id int
typename varchar(20) (ex: Desktop, Laptop, Server, etc.)
Table: assetstatus
id int
statusname varchar(20) (ex: Deployed, Inventory, Shipped, etc.)
Desired results:
AssetType Total Deployed Inventory Shipped ...
-----------------------------------------------------------
Desktop 100 75 20 5 ...
Laptop 75 56 19 1 ...
Server 60 50 10 0 ...
Some Data:
assets table:
1,hol1234,1,1
2,hol1233,1,2
3,hol3421,2,3
4,svr1234,3,1
assettypes table:
1,Desktop
2,Laptop
3,Server
assetstatus table:
1,Deployed
2,Inventory
3,Shipped
This type of transformation is called a pivot. You did not specify what database you are using so I will provide a answers for SQL Server and MySQL.
SQL Server: If you are using SQL Server 2005+ you can implement the PIVOT function.
If you have a known number of values that you want to convert to columns then you can hard-code the query.
select typename, total, Deployed, Inventory, shipped
from
(
select count(*) over(partition by t.typename) total,
s.statusname,
t.typename
from assets a
inner join assettypes t
on a.assettype = t.id
inner join assetstatus s
on a.assetstatus = s.id
) d
pivot
(
count(statusname)
for statusname in (Deployed, Inventory, shipped)
) piv;
See SQL Fiddle with Demo.
But if you have an unknown number of status values, then you will need to use dynamic sql to generate the list of columns at run-time.
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(statusname)
from assetstatus
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT typename, total,' + #cols + ' from
(
select count(*) over(partition by t.typename) total,
s.statusname,
t.typename
from assets a
inner join assettypes t
on a.assettype = t.id
inner join assetstatus s
on a.assetstatus = s.id
) x
pivot
(
count(statusname)
for statusname in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle with Demo
This can also be written using an aggregate function with a case expression:
select typename,
total,
sum(case when statusname ='Deployed' then 1 else 0 end) Deployed,
sum(case when statusname ='Inventory' then 1 else 0 end) Inventory,
sum(case when statusname ='Shipped' then 1 else 0 end) Shipped
from
(
select count(*) over(partition by t.typename) total,
s.statusname,
t.typename
from assets a
inner join assettypes t
on a.assettype = t.id
inner join assetstatus s
on a.assetstatus = s.id
) d
group by typename, total
See SQL Fiddle with Demo
MySQL: This database does not have a pivot function so you will have to use the aggregate function and a CASE expression. It also does not have windowing functions, so you will have to alter the query slightly to the following:
select typename,
total,
sum(case when statusname ='Deployed' then 1 else 0 end) Deployed,
sum(case when statusname ='Inventory' then 1 else 0 end) Inventory,
sum(case when statusname ='Shipped' then 1 else 0 end) Shipped
from
(
select t.typename,
(select count(*)
from assets a1
where a1.assettype = t.id
group by a1.assettype) total,
s.statusname
from assets a
inner join assettypes t
on a.assettype = t.id
inner join assetstatus s
on a.assetstatus = s.id
) d
group by typename, total;
See SQL Fiddle with Demo
Then if you need a dynamic solution in MySQL, you will have to use a prepared statement to generate the sql string to execute:
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'sum(CASE WHEN statusname = ''',
statusname,
''' THEN 1 else 0 END) AS `',
statusname, '`'
)
) INTO #sql
FROM assetstatus;
SET #sql
= CONCAT('SELECT typename,
total, ', #sql, '
from
(
select t.typename,
(select count(*)
from assets a1
where a1.assettype = t.id
group by a1.assettype) total,
s.statusname
from assets a
inner join assettypes t
on a.assettype = t.id
inner join assetstatus s
on a.assetstatus = s.id
) d
group by typename, total');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
See SQL Fiddle with Demo.
The result is the same for all queries in both databases:
| TYPENAME | TOTAL | DEPLOYED | INVENTORY | SHIPPED |
-----------------------------------------------------
| Desktop | 2 | 1 | 1 | 0 |
| Laptop | 1 | 0 | 0 | 1 |
| Server | 1 | 1 | 0 | 0 |
Using a non pivot compliant DBMS (Absolute Database) I was more successful using this SQL cross-tab equivalent statement:
SELECT
sub.TypeName
, SUM(sub.[Count]) AS "Total"
, SUM(CASE WHEN AssetStatus='1' THEN sub.[Count] ELSE 0 END) AS "Deployed"
, SUM(CASE WHEN AssetStatus='2' THEN sub.[Count] ELSE 0 END) AS "Inventory"
, SUM(CASE WHEN AssetStatus='3' THEN sub.[Count] ELSE 0 END) AS "Shipped"
FROM
(
SELECT
t.TypeName
, AssetStatus
, COUNT(AssetID) AS "Count"
FROM
Assets
JOIN AssetTypes t ON t.ID = AssetType
JOIN AssetStatus s ON s.ID = AssetStatus
GROUP BY t.TypeName, AssetStatus, s.StatusName
) sub
GROUP BY sub.TypeName
;
As I realized this code (above) didn't work with MySQL I adapted my code as below executing equally well in MySQL as in my current Absolute Database. The reason is the specific NULL handling avoiding the pitfall of dBase, Paradox as well as Absolute Database generously accepting COUNT(NULL) = 0 not accepted in mainstream databases.
So believing this will execute well in most databases (handling CASE ..) this is my adapted code:
SELECT
sub.TypeName
, SUM(sub.AssetCase) AS "Total"
, SUM(CASE WHEN sub.StatusName = 'Deployed' THEN sub.AssetCase ELSE 0 END) AS "Deployed"
, SUM(CASE WHEN sub.StatusName = 'Inventory' THEN sub.AssetCase ELSE 0 END) AS "Inventory"
, SUM(CASE WHEN sub.StatusName = 'Shipped' THEN sub.AssetCase ELSE 0 END) AS "Shipped"
FROM
(
SELECT
c.TypeName
, c.StatusName
, CASE WHEN a.AssetID IS NULL THEN 0 ELSE 1 END AS "AssetCase"
FROM
(
SELECT
t.ID AS tID
, t.TypeName
, s.ID AS sID
, s.StatusName
FROM
AssetTypes t, AssetStatus s
) c
LEFT JOIN Assets a
ON a.AssetType = c.tID AND a.AssetStatus = c.sID
) sub
GROUP BY
sub.TypeName
;
Best Regards
Niels Knabe
Badly phrased title my apologies.
I am trying to join a table to one of two other tables
MasterTable
SubTable
SubTableArchive
So, MasterTable contains an ID field.
SubTable and SubTableArchive contain a MasterTableId field for the join.
But, data will only ever exist in one of these SubTables. So I just want to join to whichever table has the data in it.
But the only way I know of doing it is by joining to both and using isnull's on all the fields im selecting, and its quickly becoming complicated to read (and write).
Especially because some of the fields are already wrapped in ISNULL's
SELECT M.Id, ISNULL(S.Field1, SA.field1), ISNULL(S.field2, SA.Field2),
SUM(CASE WHEN (ISNULL(S.Finished,SA.Finished)=1 AND ISNULL( ISNULL(S.ItemCode,SA.ItemCode),'')='') THEN 1 WHEN (ISNULL(S.Finished,SA.Finished)=0 AND ISNULL( ISNULL(S.AltItemCode,SA.AltItemCode),'')='') THEN 1 ELSE 0 END) AS SummaryField
FROM MAsterTable M
LEFT OUTER JOIN SubTable S ON S.MasterTableId = M.Id
LEFT OUTER JOIN SubTableArchive SA ON S.MasterTableId = M.Id
GROUP BY M.Id, ISNULL(S.Field1, SA.field1), ISNULL(S.field2, SA.Field2)
So that is working, but its not pretty.
Thats a sample, but the real queries are longer and more convoluted.
I was hoping SQL might have had some sort of conditional joining functionality built in.
Something to do what I am trying to do and leave the query itslef a little friendlier.
Another option is to use a UNION and then use INNER JOINs
SELECT M.x, S.x
FROM MAsterTable M INNER JOIN SubTable S ON S.MasterTableId = M.Id
UNION
SELECT M.x, STA.x
FROM MAsterTable M INNER JOIN SubTableArchive STA ON STA.MasterTableId = M.Id
From a maintenance point of view, if you make the above union a view, you can then apply where filters and sorts to the view, which should simplify matters.
Use sub-select or better WITH statement (code not tested):
WITH SubTable_WithArchive
AS (SELECT Field1, Field2, Finished, ItemCode, AltItemCode, MasterTableID
FROM SubTable
UNION ALL
SELECT Field1, Field2, Finished, ItemCode, AltItemCode, MasterTableID
FROM SubTableArchive
)
SELECT M.Id,
S.Field1,
S.Field2,
SUM(CASE
WHEN s.Finished = 1 AND ISNULL(s.ItemCode, '') == '' THEN 1
WHEN s.Finished = 0 AND ISNULL(s.AltItemCode, '') == '' THEN 1
ELSE 0
END)
AS SummaryField
FROM MasterTable M
LEFT OUTER JOIN SubTable_WithArchive S
ON S.MasterTableId = M.Id
GROUP BY M.Id, S.Field1, s.field2
No, unfortunately, there isn't.
Try this
SELECT M.Id, S.Field1,S.field2,
SUM(CASE WHEN S.Finished=1 AND ISNULL( S.ItemCode,'')='')
THEN 1
WHEN S.Finished=0 AND ISNULL( S.AltItemCode,'')='')
THEN 1 ELSE 0 END) AS SummaryField
FROM MAsterTable M
JOIN (
SELECT id,field1,field2,ItemCode,AltItemCode,finished
FROM subTable
UNION
SELECT id,field1,field2,ItemCode,AltItemCode,finished
FROM subTableArchive
) S ON S.id = M.Id
GROUP BY M.Id, S.Field1,S.field2
Because a ID value from MasterTable will exist in only one table (SubTable or SubTableArchive) you can use this query:
SELECT MasterTableId Id, Field1, Field2,
SUM(CASE
WHEN Finished=1 AND ItemCode IS NULL THEN 1 --OR ISNULL(ItemCode,'') = ''
WHEN Finished=0 AND AltItemCode IS NULL THEN 1 --OR ISNULL(AltItemCode,'') = ''
ELSE 0
END) AS SummaryField
FROM SubTable
GROUP BY 1, 2, 3
UNION ALL
SELECT MasterTableId Id, Field1, Field2,
SUM(CASE
WHEN Finished=1 AND ItemCode IS NULL THEN 1 --OR ISNULL(ItemCode,'') = ''
WHEN Finished=0 AND AltItemCode IS NULL THEN 1 --OR ISNULL(AltItemCode,'') = ''
ELSE 0
END) AS SummaryField
FROM SubTableArchive
GROUP BY 1, 2, 3