Remove nulls from sub totals / grand totals in tsql rollup - tsql

I currently have a script below which aggregates some data using rollup:
SELECT
CASE
WHEN GROUPING(Custodian) = 1
THEN 'Grand Total'
WHEN GROUPING(PortfolioID) = 1
THEN Custodian+''+'Total'
ELSE Custodian
END AS Custodian
, PortfolioID
, PortfolioBaseCCY
, [Date]
, SUM(AmountTotalBaseEquiv) AS AmountTotalBaseEquiv
, ExchangeRate
, AmountTotalBaseEquivUSD
, PortfolioNAVUSD
, SUM(TotalCashPctNAV) AS TotalCashPctNAV
FROM #ResultSet
WHERE TotalCashPctNAV > 5
GROUP BY Custodian
, PortfolioID
, PortfolioBaseCCY
, [Date]
, AmountTotalBaseEquiv
, ExchangeRate
, AmountTotalBaseEquivUSD
, PortfolioNAVUSD
, TotalCashPctNAV WITH ROLLUP
HAVING GROUPING_ID(Custodian
, PortfolioID
, PortfolioBaseCCY
, [Date]
, AmountTotalBaseEquiv
, ExchangeRate
, AmountTotalBaseEquivUSD
, PortfolioNAVUSD
, TotalCashPctNAV) IN (1,255,511)
ORDER BY CASE WHEN GROUPING(Custodian) = 1 THEN 2 ELSE 1 END, Custodian, TotalCashPctNAV DESC, PortfolioID
This returns data like as an example:
Custodian PortfolioID PortfolioBaseCCY Date AmountTotalBaseEquiv ExchangeRate AmountTotalBaseEquivUSD PortfolioNAVUSD TotalCashPctNAV
XXXX TEST USD 11/09/2012 85708860.21 1 85708860.21 370253861.3 23.15
XXXX Total NULL NULL NULL 85708860.21 NULL NULL NULL 23.15
ZZZZ TEST1 GBP 11/09/2012 48427.91 0.6225 77795.84 77795.84 100
ZZZZ TEST2 GBP 11/09/2012 7772.61 0.6225 12486.12 12486.12 100
ZZZZ TEST3 USD 11/09/2012 1832627.81 1 1832627.81 17343500.68 10.56
ZZZZ Total NULL NULL NULL 1888828.33 NULL NULL NULL 210.56
Grand Total NULL NULL NULL 310273031.4 NULL NULL NULL 1051.71
What i would like is for the NULLS to become '' so that only the Total label and the two summed totals are the only bits of data on that particular line, is this possible?

You can use the ISNull() function in SQL to replace NULL with a blank space like this:
SELECT
CASE
WHEN GROUPING(Custodian) = 1
THEN 'Grand Total'
WHEN GROUPING(PortfolioID) = 1
THEN Custodian+''+'Total'
ELSE Custodian
END AS Custodian
, isNUll(PortfolioID,'')
, isNull(PortfolioBaseCCY,'')
, isNull([Date],'')
, SUM(AmountTotalBaseEquiv) AS AmountTotalBaseEquiv
, isNull(ExchangeRate,'')
, isNull(AmountTotalBaseEquivUSD,'')
, isNull(PortfolioNAVUSD,'')
, SUM(TotalCashPctNAV) AS TotalCashPctNAV

If you are okay with NULL values appearing as empty strings in both data rows and total rows, you can convert all of the non-string columns to strings and use COALESCE (or ISNULL) like:
SELECT
CASE
WHEN GROUPING(Custodian) = 1
THEN 'Grand Total'
WHEN GROUPING(PortfolioID) = 1
THEN Custodian+' '+'Total'
ELSE Custodian
END AS Custodian
, COALESCE(PortfolioID,'') AS PortfolioID
, COALESCE(PortfolioBaseCCY,'') AS PortfolioBaseCCY
, COALESCE(CONVERT(char(10),[Date],101),'') AS [Date]
, SUM(AmountTotalBaseEquiv) AS AmountTotalBaseEquiv
, COALESCE(CONVERT(char(10),ExchangeRate),'') AS ExchangeRate
, COALESCE(CONVERT(char(20),AmountTotalBaseEquivUSD),'') AS AmountTotalBaseEquivUSD
, COALESCE(CONVERT(char(20),PortfolioNAVUSD),'') AS PortfolioNAVUSD
, SUM(TotalCashPctNAV) AS TotalCashPctNAV
FROM #ResultSet
WHERE TotalCashPctNAV > 5
GROUP BY Custodian
, PortfolioID
, PortfolioBaseCCY
, [Date]
, AmountTotalBaseEquiv
, ExchangeRate
, AmountTotalBaseEquivUSD
, PortfolioNAVUSD
, TotalCashPctNAV WITH ROLLUP
HAVING GROUPING_ID(Custodian
, PortfolioID
, PortfolioBaseCCY
, [Date]
, AmountTotalBaseEquiv
, ExchangeRate
, AmountTotalBaseEquivUSD
, PortfolioNAVUSD
, TotalCashPctNAV) IN (1,255,511)
ORDER BY CASE WHEN GROUPING(Custodian) = 1 THEN 2 ELSE 1 END, Custodian, TotalCashPctNAV
Otherwise, you can use a case expression, as you did with Custodian, to determine which rows are totals. For example:
CASE WHEN GROUPING(Custodian) + GROUPING(PortfolioId) > 0 THEN ''
ELSE PortfolioID END AS PortfolioID

Related

Moving grouped MEDIAN / Get the MEDIAN of specific months from the past IN T-SQL

Let's say I have a table:
DATE
ID
VALUE
01.2010
1
100
02.2010
1
200
...
...
...
12.2010
1
300
01.2011
1
150
02.2011
1
250
...
...
...
12.2011
1
350
01.2012
1
200
02.2012
1
300
...
...
...
12.2012
1
400
I want to get a median of VALUE grouped by months i.e. get something like
DATE
ID
VALUE
MEDIAN
01.2010
1
100
100
02.2010
1
200
200
...
...
...
...
12.2010
1
300
300
01.2011
1
150
125 = (100+150)/2
02.2011
1
250
225 = (200+250)/2
...
...
...
...
12.2011
1
350
325 = (300+350)/2
01.2012
1
200
150
02.2012
1
300
250
...
...
...
...
12.2012
1
400
350
I have more ID in table so I would like to get this result for every ID.
I have tried doing
SELECT PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY VALUE) OVER (PARTITION BY Id, MONTH(Date) ORDER BY Date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
but I get "The function 'PERCENTILE_CONT' may not have a window frame.
I've also tried the following (but also without any results):
SELECT PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY VALUE)
OVER (PARTITION BY Id, MONTH(Date))
FROM tab1 LEFT JOIN tab2
ON tab1.key = tab2.key
WHERE tab1.Date BETWEEN Min(Date) AND tab2.Date
EDIT
So far I have resolved it with
SELECT (CASE WHEN Date =2010 THEN PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY CASE WHEN Date = 2010 THEN VALUE ELSE NULL) OVER (PARTITION BY Id, MONTH(Date)) ELSE 0 END) +
(CASE WHEN Date =2011 THEN PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY CASE WHEN Date <= 2011 THEN VALUE ELSE NULL) OVER (PARTITION BY Id, MONTH(Date)) ELSE 0 END) +
(CASE WHEN Date =2012 THEN PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY CASE WHEN Date <= 2012 THEN VALUE ELSE NULL) OVER (PARTITION BY Id, MONTH(Date)) ELSE 0 END)
FROM tab1
But to be honest, I would like to have an resolution without assumption of a priori knowledge of dates. I've thought about WHILE LOOP and updating column while #MinYear <= #MaxYear where in every iteration #MinYear = #MinYear+1 but in this case I would have to create temporary tables which I'm trying to avoid.
My idea is to use (Value1+value2)/2 as median as your requirement is little complicated.
CREATE TABLE MedianData
(
[Date] VARCHAR(100)
,ID INT
,[Value] INT
)
INSERT INTO MedianData VALUES ('01.2010', 1, 100)
,('02.2010', 1, 200)
,('12.2010', 1, 300)
,('01.2011', 1, 150)
,('02.2011', 1, 250)
,('12.2011', 1, 350)
,('01.2012', 1, 200)
,('02.2012', 1, 300)
,('12.2012', 1, 400)
SELECT *
,ROW_NUMBER() OVER ( PARTITION BY Substring([Date],1,2 ) ORDER BY [Date] ) AS [row]
,Substring([Date],1,2 ) as [MONTH]
INTO #Temp_tbl2
FROM MedianData
SELECT
A.Date
,A.ID
,A.[Value]
--Logic is applied here. I used (Value1+value2)/2 as median
,CASE WHEN A.[row] = 3 THEN ( A.[Value] + ( SELECT T.[Value] FROM #Temp_tbl2
T where T.[MONTH] = Substring(A.[Date],1,2 ) AND T.[row] = 1 ) )/2
WHEN A.[row] != 1 THEN (A.total/2)
ELSE A.total END as [Median]
INTO #Temp_table
FROM
(
SELECT *
,ROW_NUMBER() OVER ( PARTITION BY Substring([Date],1,2 ) ORDER BY [Date] ) AS [row]
,SUM ([Value] ) OVER ( PARTITION BY Substring([Date],1,2 ) ORDER BY [Date] ) AS [total]
FROM MedianData
) AS A
--to make the table data order
SELECT MedianData.*, #Temp_table.Median
FROM MedianData
INNER JOIN #Temp_table
ON MedianData.[Date] = #Temp_table.[Date]
drop table #Temp_table
drop table #Temp_tbl2

Imported Teradata date into SAS as Character (1/1/17)- how can i convert to SAS date9.?

This is how I import from teradata:
libname DDWP_PLT teradata
user='xxxx'
password='xxxx'
schema='DDWP_PLT_V'
server='phxtdpd2cop1.directv.com'
FASTEXPORT=YES;
PROC SQL;
create table mob_base as
select ctn
, ban
, intrxn_id
, sasdate
, start_time
, sessionstarttime
, agent_ID
, work_group as agent_skill
, tasktype
, taskresolution
, need
, need2
, need3
, disposition
, note
from ddwp_plt.ATT_MOBILITY_CLARIFY_DTL_VW
where start_time between '2016-12-29 00:00:00' and '2017-01-31 23:59:59'
and start_time = sessionstarttime
order by ctn
, sessionstarttime
;
QUIT;
Do I need to make it numeric first and then turn it into date?
I've tried:
format=date9.
INPUT(CATS(t1.column),BEST.)
Char = char X 1
these all resulted in no output

Rolling sum per time interval per group

Table, data and task as follows.
See SQL-Fiddle-Link for demo-data and estimated results.
create table "data"
(
"item" int
, "timestamp" date
, "balance" float
, "rollingSum" float
)
insert into "data" ( "item", "timestamp", "balance", "rollingSum" ) values
( 1, '2014-02-10', -10, -10 )
, ( 1, '2014-02-15', 5, -5 )
, ( 1, '2014-02-20', 2, -3 )
, ( 1, '2014-02-25', 13, 10 )
, ( 2, '2014-02-13', 15, 15 )
, ( 2, '2014-02-16', 15, 30 )
, ( 2, '2014-03-01', 15, 45 )
I need to get all rows in an defined time interval. The above table doesn't hold a record per item for each possible date - only dates on which changes applied are recorded ( it is possible that there are n rows per timestamp per item )
If the given interval does not fit exactly on stored timestamps, the latest timestamp before startdate ( nearest smallest neighbour ) should be used as start-balance/rolling-sum.
estimated results ( time interval: startdate = '2014-02-13', enddate = '2014-02-20' )
"item", "timestamp" , "balance", "rollingSum"
1 , '2014-02-13' , -10 , -10
1 , '2014-02-15' , 5 , -5
1 , '2014-02-20' , 2 , -3
2 , '2014-02-13' , 15 , 15
2 , '2014-02-16' , 15 , 30
I checked questions like this and googled a lot, but didn't found a solution yet.
I don't think it's a good idea to extend "data" table with one row per missing date per item, thus the complete interval ( smallest date <-----> latest date per item may expand over several years ).
Thanks in advance!
select sum(balance)
from table
where timestamp >= (select max(timestamp) from table where timestamp <= 'startdate')
and timestamp <= 'enddate'
Don't know what you mean by rolling-sum.
here is an attempt. Seems it gives the right result, not so beautiful. Would have been easier in sqlserver 2012+:
declare #from date = '2014-02-13'
declare #to date = '2014-02-20'
;with x as
(
select
item, timestamp, balance, row_number() over (partition by item order by timestamp, balance) rn
from (select item, timestamp, balance from data
union all
select distinct item, #from, null from data) z
where timestamp <= #to
)
, y as
(
select item,
timestamp,
coalesce(balance, rollingsum) balance ,
a.rollingsum,
rn
from x d
cross apply
(select sum(balance) rollingsum from x where rn <= d.rn and d.item = item) a
where timestamp between '2014-02-13' and '2014-02-20'
)
select item, timestamp, balance, rollingsum from y
where rollingsum is not null
order by item, rn, timestamp
Result:
item timestamp balance rollingsum
1 2014-02-13 -10,00 -10,00
1 2014-02-15 5,00 -5,00
1 2014-02-20 2,00 -3,00
2 2014-02-13 15,00 15,00
2 2014-02-16 15,00 30,00

Create Top 10 but with everything except Top 9 in Other

What i would like to do is have a Top 10, but the 10th entry is called "Other" with the sum of everything bar the top 9 within it and has a total. So basically it looks like this:
ReportingDate FundCode Currency Duration Contribution Percentage
31/10/2012 1111 Malaysian Ringgit 0.5 14.6
31/10/2012 1111 Turkish Lira 0.3 13.5
31/10/2012 1111 Russian Rouble 0.5 11.9
31/10/2012 1111 Indunesian Rupiah 0.6 11.7
31/10/2012 1111 Mexican Peso 0.6 11.7
31/10/2012 1111 Polish Zloty 0.3 10.2
31/10/2012 1111 Mexican Peso 0.4 10.1
31/10/2012 1111 Polish Zloty 0.3 9.9
31/10/2012 1111 South African Rand 0.2 5.8
31/10/2012 1111 Brazilian Real 0.3 2.0
31/10/2012 1111 Other 0.6 -1.4
31/10/2012 1111 Total 4.6 100.0
My code currently looks like this:
;;WITH CTE AS
(
SELECT
ReportingDate
, PortfolioID
, DV.dmv_nme AS Currency
, RANK() OVER (PARTITION BY PortfolioID ORDER BY SUM(Percentage) DESC) AS [Rank]
, ISNULL(CAST(SUM(DurationContribution)/100.0 AS DECIMAL(22,1)),0) AS [Duration Contribution]
, CAST(SUM(Percentage) AS DECIMAL(22,1)) AS [Weight]
FROM #Worktable as WT
INNER JOIN dw_domain_value AS DV
ON DV.dmv_value = WT.Currency
AND DV.data_cls_num = 2
GROUP BY WT.ReportingDate
, WT.PortfolioID
, DV.dmv_nme
)
SELECT
ReportingDate
, PortfolioID
, Currency
, [Rank]
, [Duration Contribution]
, [Weight]
FROM CTE
WHERE [Rank] <= 10
ORDER BY ReportingDate, PortfolioID, [Rank], [Weight] DESC
So this gives me the top 10 fine. So how could i get it so that the final 10th line is "Other" with everything bar the top 9 summed within it, and also include a total at the end?
Here is one solution. But you may be able to move it into the CTE for better performance.
;;WITH CTE AS
(
SELECT
ReportingDate
,PortfolioID
,DV.dmv_nme AS Currency
,RANK() OVER (PARTITION BY PortfolioID ORDER BY SUM(Percentage) DESC) AS [Rank]
,ISNULL(CAST(SUM(DurationContribution)/100.0 AS DECIMAL(22,1)),0) AS [Duration Contribution]
,CAST(SUM(Percentage) AS DECIMAL(22,1)) AS [Weight]
FROM #Worktable as WT
INNER JOIN dw_domain_value AS DV
ON DV.dmv_value = WT.Currency
AND DV.data_cls_num = 2
GROUP BY WT.ReportingDate
, WT.PortfolioID
, DV.dmv_nme
)
SELECT
ReportingDate
, PortfolioID
, CASE WHEN [Rank] <= 10 THEN Currency ELSE 'Total' END As Currency
, SUM([Duration Contribution]) As [Duration Contribution]
, SUM([Weight]) As [Weight]
FROM CTE
GROUP BY
ReportingDate
, PortfolioID
, CASE WHEN [Rank] <= 10 THEN Currency ELSE 'Total' END As Currency
WITH ROLLUP
ORDER BY ReportingDate, PortfolioID, [Rank], [Weight] DESC
For this i decided i couldn't use CTE, so ended up having to insert these section by section into a temp table as so:
/* Include only top 9 */
INSERT INTO #FinalOutput
SELECT
ReportingDate
, PortfolioID
, PortfolioNme
, Currency
, [Rank]
, DurationContribution
, [Weight]
FROM #WorktableGrouped
WHERE [Rank] <= 9
ORDER BY ReportingDate, PortfolioID, [Rank], [Weight] DESC
/* Aggregate everything outside the top 9 into other */
INSERT INTO #FinalOutput
SELECT
ReportingDate
, PortfolioID
, PortfolioNme
, 'Other' AS Currency
, 10 AS [Rank]
, SUM(DurationContribution) AS DurationContribution
, SUM([Weight]) AS [Weight]
FROM #WorktableGrouped
WHERE [Rank] > 9
GROUP BY ReportingDate, PortfolioID, PortfolioNme
ORDER BY ReportingDate, PortfolioID, [Rank], [Weight] DESC
SELECT * FROM #FinalOutput
/* Final Select with roll up for total per portfolio */
SELECT
ReportingDate
, PortfolioID
, PortfolioNme
, CASE
WHEN GROUPING_ID(ReportingDate, PortfolioID, PortfolioNme, Currency, [Rank]) = 3 THEN 'Total'
ELSE Currency
END AS Currency
, CASE
WHEN GROUPING_ID(ReportingDate, PortfolioID, PortfolioNme, Currency, [Rank]) = 3 THEN 11
ELSE [Rank]
END AS [Rank]
, ISNULL(CAST(SUM(DurationContribution) AS DECIMAL(22,1)),0) AS [Duration Contribution]
, CAST(SUM([Weight]) AS DECIMAL(22,1)) AS [Weight]
--, GROUPING_ID(ReportingDate, PortfolioID, PortfolioNme, Currency, [Rank])
FROM #FinalOutput
GROUP BY ReportingDate
, PortfolioID
, PortfolioNme
, Currency
, [Rank] WITH ROLLUP
HAVING GROUPING_ID(ReportingDate, PortfolioID, PortfolioNme, Currency, [Rank]) IN (0,3)
ORDER BY ReportingDate, PortfolioID, [Rank]

Merge records that meet a particular criteria - TSQL

SQL Server 2008
Hi
I have an issue where i have two of the same accounts which i would like to merge together, the list looks like below:
AcctID AcctType AcctSubType Curr TransType Amount
1 CCY SET EUR Opening 1000
1 CCY SET EUR BUY -100
1 CCY SET EUR SEL 100
1 CCY SET EUR Closing 1000
2 CCY SET EUR Opening 2000
2 CCY SET EUR SEL 100
2 CCY SET EUR Closing 2100
3 CCY INC EUR Opening 1000
3 CCY INC EUR SEL 200
3 CCY INC EUR BUY -100
3 CCY INC EUR Closing 1100
So basically i would like to merge it so it looks like this:
AcctID AcctType AcctSubType Curr TransType Amount
1 CCY SET EUR Opening 3000
1 CCY SET EUR BUY -100
1 CCY SET EUR SEL 100
2 CCY SET EUR SEL 100
1 CCY SET EUR Closing 3100
3 CCY INC EUR Opening 1000
3 CCY INC EUR SEL 200
3 CCY INC EUR BUY -100
3 CCY INC EUR Closing 1100
However i can't think of the best way to code this in SQL in my stored proc. I can only really think of a merge or a union, but i only want it to merge where there are two capital accounts, a capital account is signified by the 'CCY' AcctType and AcctSubType 'SET'.
A point in the right direction would be much appreciated.
Thanks
EDIT: Actual stored proc
CREATE TABLE #Workbook
(ID INT
,PortfolioID VARCHAR(20)
,PortfolioName VARCHAR(255)
,InstrumentID VARCHAR(20)
, IssueID VARCHAR(50)
, CashAccountInstrumentID VARCHAR(20)
, CashAccountName VARCHAR(255)
, CashAccountType CHAR(3)
, CashAccountSubType CHAR(3)
, TransactionClass VARCHAR(10)
, TransactionType VARCHAR(20)
, EffPostDate DATETIME
, TradeDate DATETIME
, SettleDate DATETIME
, CcySettle VARCHAR(3)
, DateItem DATETIME
, RunningTotalDate DATETIME
, Detail VARCHAR(300)
, ProceedsLocal FLOAT
, Total FLOAT
, RunningTotal FLOAT
, ReconValue FLOAT
, ReconNotes VARCHAR(50)
, RecordType VARCHAR(1)
, Sequence INT
, RecordNum INT
, TransactionID VARCHAR(20)
)
INSERT INTO #Workbook
EXEC dbo.usp_Generic_ReconCashAccount_NEW '2010-10-01', '2010-10- 07', '', 'OOGENHF', 'SS','','','','',''
DELETE FROM #Workbook
WHERE TransactionType = 'BBA'
OR (ProceedsLocal = 0 AND RecordType = 'T')
;;with needed as
(
select * from #Workbook w
WHERE w.CashAccountType = 'CCY'
AND w.CashAccountSubType = 'SET'
AND w.TransactionType In ('Opening','Closing')
AND w.CcySettle = w.CcySettle
)
select
min(ID)
, PortfolioID
, PortfolioName
, InstrumentID
, IssueID
, min(CashAccountInstrumentID) as CashAccountInstrumentID
, min(CashAccountName) as CashAccountName
, CashAccountType
, CashAccountSubType
, TransactionClass
, TransactionType
, EffPostDate
, TradeDate
, SettleDate
, CcySettle
, DateItem
, RunningTotalDate
, Detail
, sum(ProceedsLocal) as ProceedsLocal
, sum(Total) as Total
, sum(RunningTotal) as RunningTotal
, sum(ReconValue) as ReconValue
, ReconNotes
, RecordType
, Sequence
, RecordNum
, TransactionID
from needed
group by ID,PortfolioID,PortfolioName,InstrumentID,IssueID,CashAccountInstrumentID,CashAccountName,CashAccountType,CashAccountSubType,TransactionClass,TransactionType,EffPostDate,TradeDate,SettleDate
,CcySettle, DateItem,RunningTotalDate,Detail,ProceedsLocal,Total,RunningTotal,ReconValue,ReconNotes,RecordType,Sequence,RecordNum,TransactionID
UNION ALL
(
select * from #Workbook
except
select * from needed
)
drop table #Workbook
How about this:
;with test (AcctID, AcctType, AcctSubType, Curr, TransType, Amount)
as
(
select 1, 'CCY', 'SET', 'EUR', 'Opening', 1000
union all
select 1, 'CCY', 'SET', 'EUR', 'BUY', -100
union all
select 1, 'CCY', 'SET', 'EUR', 'SEL', 100
union all
select 1, 'CCY', 'SET', 'EUR', 'Closing', 1000
union all
select 2, 'CCY', 'SET', 'EUR', 'Opening', 2000
union all
select 2, 'CCY', 'SET', 'EUR', 'SEL', 100
union all
select 2, 'CCY', 'SET', 'EUR', 'Closing', 2100
union all
select 3, 'CCY', 'INC', 'EUR', 'Opening', 1000
union all
select 3, 'CCY', 'INC', 'EUR', 'SEL', 200
union all
select 3, 'CCY', 'INC', 'EUR', 'BUY', -100
union all
select 3, 'CCY', 'INC', 'EUR', 'Closing', 1100
union all
select 4, 'CCY', 'SET', 'SEK', 'Opening', 2000
union all
select 4, 'CCY', 'SET', 'SEK', 'SEL', 100
union all
select 4, 'CCY', 'SET', 'SEK', 'Closing', 2100
)
, needed as
(
select *
from test
where AcctType = 'CCY'
and AcctSubType = 'SET'
and TransType in ('Opening','Closing')
)
select min(AcctID) as AcctID
,AcctType
,AcctSubType
,Curr
,TransType
,sum(Amount) as Amount
from needed
group by
AcctType
,AcctSubType
,Curr
,TransType
union all
(
select *
from test
except
select *
from needed
)
EDIT: Updated to support multiple currencys.
The only change is to wrap the final SELECT - EXCEPT in parantheses.