Group accounts by 1 condition vs 2 conditions - sql

I am trying to get a count of pat_id that were either only "Sent" or only "Sent and Received"
Here is my query so far
select pat_id,
(case when addr_msg_actn_c in(1) then 'Sent' else convert(varchar(10),'Received') end) [Status]
from pat_addr_verif
where pat_id in ('Z1000354','Z1000363','Z1000392','Z1000394')
group by pat_id, addr_msg_actn_c
order by pat_id
Which generates this output
However, I'd like to simplify the output to display like this

Here's a small proof of concept. You can generate a case statement on the sum of a value to choose which text string you want.
CREATE TABLE #SentReceive (
Pat_ID NVARCHAR(8)
,[status] TINYINT
)
INSERT INTO #SentReceive
VALUES ( 'Z1000354' ,1 )
,( 'Z1000354' ,1 )
,( 'Z1000363' ,1 )
,( 'Z1000363' ,2 )
,( 'Z1000392' ,2 )
,( 'Z1000394' ,1 )
,( 'Z1000394' ,2 )
SELECT pat_id
,sentstatus = CASE sum(distinct [status])
WHEN 1
THEN 'Sent'
WHEN 2
THEN 'Received'
WHEN 3
THEN 'Sent and Received'
END
FROM #SentReceive
GROUP BY pat_id
ORDER BY pat_id

create table SentReceive
(
Pat_ID nvarchar(8),
SentOnly nvarchar(8)
)
insert SentReceive
values
('Z1000354','Sent'),
('Z1000363','Sent'),
('Z1000363','Received'),
('Z1000392','Sent'),
('Z1000394','Sent'),
('Z1000394','Received')
select distinct a.Pat_ID,
case
when b.SentOnly is not null then
case a.SentOnly
when 'Received' then b.SentOnly + ' and ' + a.SentOnly
else a.SentOnly + ' and ' + b.SentOnly
end
else a.SentOnly
end as Status
from SentReceive a
left join SentReceive b on a.Pat_ID = b.Pat_ID and a.SentOnly <> b.SentOnly
I did a quickie with only the available data that you provided. If there are other variations in your dataset then you will need to account for that and it may or may not change what I came up with. Good luck!

Here is what I came up with:
;with cte as
(
select 1 as _order, 'Z1000354' as pat_id, 'Sent' as SentOnly
union select 2, 'Z1000363', 'Sent'
union select 3, 'Z1000363', 'Received'
union select 4, 'Z1000392', 'Sent'
union select 5, 'Z1000393', 'Received'
union select 5, 'Z1000394', 'Sent'
union select 6, 'Z1000394', 'Received'
)
select pat_id, replace(_Status,',',' and') as _Status
from(
select distinct pat_id,
STUFF((Select ', '+SentOnly
from cte T2
where T1.pat_id=T2.pat_id
FOR XML PATH('')),1,1,'') as _Status from cte T1
) a
where _Status in (' Sent', ' Sent, Received')

Final query with some help from above posters:
select distinct a.pat_id,
case when b.ADDR_MSG_ACTN_C is not null
then 'Sent and Received' else 'Sent Only' end as Status
from pat_addr_verif a
left join pat_addr_verif b on a.pat_id = b.pat_id and
a.ADDR_MSG_ACTN_C <> b.ADDR_MSG_ACTN_C
where a.pat_id in ('Z1000354','Z1000363','Z1000392','Z1000394')
Correct Final Output

Related

Capturing specific Status Change Date from Each group in BIGQUERY

I want to Capture specific Status Change Date from Each group and show them in a single row.PFB snapshot of the requirement
enter image description here
I have basic SQL knowledge but try my best, please verify and review.
code for MS SQL
CREATE TABLE [data] (package NVARCHAR(250), status NVARCHAR(250), date NVARCHAR(25))
INSERT INTO data (package,status,date)
SELECT 'sony', 'register', 'D1' UNION
SELECT 'sony', 'renew', 'D2' UNION
SELECT 'sony', 'cancel', 'D3' UNION
SELECT 'starSports', 'register', 'D3' UNION
SELECT 'starSports', 'renew', 'D4' UNION
SELECT 'starSports', 'modified', 'D5'
WITH cte_data
AS (
SELECT package,
(CASE WHEN status = 'register' THEN [date] ELSE '' END) AS register_date,
(CASE WHEN status = 'renew' THEN [date] ELSE '' END) AS renew_date
FROM data )
select t.package, t.register_date, d.renew_date
from cte_data t
INNER JOIN cte_data d ON d.package = t.package
where t.register_date<>'' AND d.renew_date <>''
Consider below approach
select * from your_table
pivot (any_value(date) Date for status in ('Register', 'Renew'))
if applied to sample data in your question - output is

SQL Server query result for all shipping products

I have a product table as below.
Where I want to get each product detail where its product_ship_flag is "YES" for all the products of same the order_number. If any of the product_ship_flag is "No" then the whole order_number should not be in the output. If all product_ship_flag as "No" then should not be in the output.
The Output will look like:
Thanks in advance.
Edited: Few members are not even trying to understand the problem, And some are not able to answer the problem so devoting the question. Please show some sportiness.
Try this.
If the inner query does not return anything for that order that means all the product has flag_ship equal to Yes
select * from product p
where not exists (
select '1' from product p2
where p2.order_number = p1.order_number
and p2.product_ship_flag = 'No')
Isn't that a basic query like this works?
Select * from product where product_ship_flag = 'Yes' group by order_number
Try with this query:
select * from product where orderno not in (select orderno, count(case when product_ship_flaf='No' then 1 end) from product
group by orderno
having count(case when product_ship_flaf='No' then 1 end)>=1)A
#irfan_m, the logic can be captured with the MIN operator. When you do a subquery and using a column called flag for example. You can easily pull out the orders based on your requirement.
See a mock up below:
DECLARE #Producttbl TABLE (id int, order_number varchar(20), product_id varchar(20), stock_cnt int, product_ship_flag VARCHAR(3))
INSERT INTO #Producttbl
SELECT 1,'001','SKU1', 1, 'Yes' union all
SELECT 2,'001','SKU2', 2, 'Yes' union all
SELECT 3,'001','SKU3', 1, 'No' union all
SELECT 4,'002','SKU1', 1, 'Yes' union all
SELECT 5,'002','SKU2', 2, 'Yes' union all
SELECT 6,'003','SKU1', 1, 'No' union all
SELECT 7,'003','SKU2', 2, 'No'
SELECT *
FROM
#Producttbl P
JOIN (
SELECT order_number, Flag=MIN(product_ship_flag)
--product_ship_flag is
---"YES" : for all the products of same the order_number
--If any of the product_ship_flag is "No" then the whole order_number should not be in the output.
--If all product_ship_flag as "No" then should not be in the output.
FROM
#Producttbl
GROUP BY
order_number
)S ON
S.order_number=P.order_number
WHERE
S.Flag='Yes'
see result below:
A left join could be handy in this case.
DECLARE #ProducttblTEST TABLE (id int, order_number varchar(20), product_id
varchar(20), stock_cnt int, product_ship_flag VARCHAR(3))
INSERT INTO #ProducttblTEST
SELECT 1,'001','SKU1', 1, 'Yes' union all
SELECT 2,'001','SKU2', 2, 'Yes' union all
SELECT 3,'001','SKU3', 1, 'No' union all
SELECT 4,'002','SKU1', 1, 'Yes' union all
SELECT 5,'002','SKU2', 2, 'Yes' union all
SELECT 6,'003','SKU1', 1, 'No' union all
SELECT 7,'003','SKU2', 2, 'No'
select * from #ProducttblTEST a
left join (select * from #ProducttblTEST where product_ship_flag = 'no') b on
a.order_number = b.order_number
where a.product_ship_flag = 'yes' and b.order_number is null

How to merge two columns from CASE STATEMENT of DIFFERENT CONDITION

My expected result should be like
----invoiceNo----
T17080003,INV14080011
But right now, I've come up with following query.
SELECT AccountDoc.jobCode,AccountDoc.shipmentSyskey,AccountDoc.docType,
CASE AccountDoc.docType
WHEN 'M' THEN
JobInvoice.invoiceNo
WHEN 'I' THEN
(STUFF((SELECT ', ' + RTRIM(CAST(AccountDoc.docNo AS VARCHAR(20)))
FROM AccountDoc LEFT OUTER JOIN JobInvoice
ON AccountDoc.principalCode = JobInvoice.principalCode AND
AccountDoc.jobCode = JobInvoice.jobCode
WHERE (AccountDoc.isCancelledByCN = 0)
AND (AccountDoc.docType = 'I')
AND (AccountDoc.jobCode = #jobCode)
AND (AccountDoc.shipmentSyskey = #shipmentSyskey)
AND (AccountDoc.principalCode = #principalCode) FOR XML
PATH(''), TYPE).value('.','NVARCHAR(MAX)'),1,2,' '))
END AS invoiceNo
FROM AccountDoc LEFT OUTER JOIN JobInvoice
ON JobInvoice.principalCode = AccountDoc.principalCode AND
JobInvoice.jobCode = AccountDoc.jobCode
WHERE (AccountDoc.jobCode = #jobCode)
AND (AccountDoc.isCancelledByCN = 0)
AND (AccountDoc.shipmentSyskey = #shipmentSyskey)
AND (AccountDoc.principalCode = #principalCode)
OUTPUT:
----invoiceNo----
T17080003
INV14080011
Explanation:
I want to select docNo from table AccountDoc if AccountDoc.docType = I.
Or select invoiceNo from table JobInvoice if AccountDoc.docType = M.
The problem is what if under same jobCode there have 2 docType which are M and I, how I gonna display these 2 invoices?
You can achieve this by using CTE and FOR XML. below is the sample code i created using similar tables you have -
Create table #AccountDoc (
id int ,
docType char(1),
docNo varchar(10)
)
Create table #JobInvoice (
id int ,
invoiceNo varchar(10)
)
insert into #AccountDoc
select 1 , 'M' ,'M1234'
union all select 2 , 'M' ,'M2345'
union all select 3 , 'M' ,'M3456'
union all select 4 , 'I' ,'I1234'
union all select 5 , 'I' ,'I2345'
union all select 6 , 'I' ,'I3456'
insert into #JobInvoice
select 1 , 'INV1234'
union all select 2 , 'INV2345'
union all select 3 , 'INV3456'
select *
from #AccountDoc t1 left join #JobInvoice t2
on t1.id = t2.id
with cte as
(
select isnull( case t1.docType WHEN 'M' THEN t2.invoiceNo WHEN 'I' then
t1.docNo end ,'') invoiceNo
from #AccountDoc t1 left join #JobInvoice t2
on t1.id = t2.id )
select invoiceNo + ',' from cte For XML PATH ('')
You need to pivot your data if you have situations where there are two rows, and you want two columns. Your sql is a bit messy, particularly the bit where you put an entire select statement inside a case when in the select part of another query. These two queries are virtually the same, you should look for a more optimal way of writing them. However, you can wrap your entire sql in the following:
select
Jobcode, shipmentsyskey, [M],[I]
from(
--YOUR ENTIRE SQL GOES HERE BETWEEN THESE BRACKETS. Do not alter anything else, just paste your entire sql here
) yoursql
pivot(
max(invoiceno)
for docType in([M],[I])
)pvt

Sql Server 2008 - PIVOT without Aggregation Function

I know you've got multiple topics touching on this. But, I havent found one that addressed my needs. I need to (on demand) pivot select deep table data to a wide output table. The gotcha in this is that I cannot use an aggregate with Pivot because it eats responses that are needed in the output. I have worked up to a solution, but I don't think it's the best because it will require umpteen left joins to work. I've included all attempts and notes as follows:
-- Sql Server 2008 db.
-- Deep table structure (not subject to modification) contains name/value pairs with a userId as
-- foreign key. In many cases there can be MORE THAN ONE itemValue given by the user for the
-- itemName such as if asked their race, can answer White + Hispanic, etc. Each response is stored
-- as a seperate record - this cannot currently be changed.
-- Goal: pivot deep data to wide while also compressing result
-- set down. Account for all items per userId, and duplicating
-- column values (rather than show nulls) as applicable
-- Sample table to store some data of both single and multiple responses
DECLARE #testTable AS TABLE(userId int, itemName varchar(50), itemValue varchar(255))
INSERT INTO #testTable
SELECT 1, 'q01', '1-q01 Answer'
UNION SELECT 1, 'q02', '1-q02 Answer'
UNION SELECT 1, 'q03', '1-q03 Answer 1'
UNION SELECT 1, 'q03', '1-q03 Answer 2'
UNION SELECT 1, 'q03', '1-q03 Answer 3'
UNION SELECT 1, 'q04', '1-q04 Answer'
UNION SELECT 1, 'q05', '1-q05 Answer'
UNION SELECT 2, 'q01', '2-q01 Answer'
UNION SELECT 2, 'q02', '2-q02 Answer'
UNION SELECT 2, 'q03', '2-q03 Answer 1'
UNION SELECT 2, 'q03', '2-q03 Answer 2'
UNION SELECT 2, 'q04', '2-q04 Answer'
UNION SELECT 2, 'q05', '2-q05 Answer'
SELECT 'Raw Data'
SELECT * FROM #TestTable
SELECT 'Using Pivot - shows aggregate result of itemValue per itemName - eats others'
; WITH Data AS (
SELECT
[userId]
, [itemName]
, [itemValue]
FROM
#testTable
)
SELECT
[userId]
, [q02]
, [q03]
, [q05]
FROM
Data
PIVOT
(
MIN(itemValue) -- Aggregate function eats needed values.
FOR itemName in ([q02], [q03], [q05])
) AS PivotTable
SELECT 'Aggregate with Grouping - Causes Null Values'
SELECT
DISTINCT userId
,[q02] = Max(CASE WHEN itemName = 'q02' THEN itemValue END)
,[q03] = Max(CASE WHEN itemName = 'q03' THEN itemValue END)
,[q05] = Max(CASE WHEN itemName = 'q05' THEN itemValue END)
FROM
#testTable
WHERE
itemName in ('q02', 'q03', 'q05') -- Makes it a hair quicker
GROUP BY
userId -- If by userId only, it only gives 1 row PERIOD = BAD!!
, [itemName]
, [itemValue]
SELECT 'Multiple Left Joins - works properly but bad if pivoting 175 columns or so'
; WITH Data AS (
SELECT
userId
,[itemName]
,[itemValue]
FROM
#testTable
WHERE
itemName in ('q02', 'q03', 'q05') -- Makes it a hair quicker
)
SELECT
DISTINCT s1.userId
,[q02] = s2.[itemValue]
,[q03] = s3.[itemValue]
,[q05] = s5.[itemValue]
FROM
Data s1
LEFT JOIN Data s2
ON s2.userId = s1.userId
AND s2.[itemName] = 'q02'
LEFT JOIN Data s3
ON s3.userId = s1.userId
AND s3.[itemName] = 'q03'
LEFT JOIN Data s5
ON s5.userId = s1.userId
AND s5.[itemName] = 'q05'
So the bottom query is the only one (so far) that does what I need it to do, but the LEFT JOIN's WILL get out of hand and cause performance issues when I use actual item names to pivot. Any recommendations are appreciated.
I think you'll have to stick with joins, because joins are exactly the way of producing results like the one you are after. The purpose of a join is to combine row sets together (on a condition or without any), and your target output is nothing else than a combination of subsets of rows.
However, if the majority of questions always have single responses, you could substantially reduce the number of necessary joins. The idea is to join only multiple-response groups as separate row sets. As for the single-response items, they are joined only as part of the entire dataset of target items.
An example should better illustrate what I might poorly describe verbally. Assuming there are two potentially multiple-response groups in the source data, 'q03' and 'q06' (actually, here's the source table:
DECLARE #testTable AS TABLE(
userId int,
itemName varchar(50),
itemValue varchar(255)
);
INSERT INTO #testTable
SELECT 1, 'q01', '1-q01 Answer'
UNION SELECT 1, 'q02', '1-q02 Answer'
UNION SELECT 1, 'q03', '1-q03 Answer 1'
UNION SELECT 1, 'q03', '1-q03 Answer 2'
UNION SELECT 1, 'q03', '1-q03 Answer 3'
UNION SELECT 1, 'q04', '1-q04 Answer'
UNION SELECT 1, 'q05', '1-q05 Answer'
UNION SELECT 1, 'q06', '1-q06 Answer 1'
UNION SELECT 1, 'q06', '1-q06 Answer 2'
UNION SELECT 1, 'q06', '1-q06 Answer 3'
UNION SELECT 2, 'q01', '2-q01 Answer'
UNION SELECT 2, 'q02', '2-q02 Answer'
UNION SELECT 2, 'q03', '2-q03 Answer 1'
UNION SELECT 2, 'q03', '2-q03 Answer 2'
UNION SELECT 2, 'q04', '2-q04 Answer'
UNION SELECT 2, 'q05', '2-q05 Answer'
UNION SELECT 2, 'q06', '2-q06 Answer 1'
UNION SELECT 2, 'q06', '2-q06 Answer 2'
;
which is same as the table in the original post, but with added 'q06' items), the resulting script could be like this:
WITH ranked AS (
SELECT
*,
rn = ROW_NUMBER() OVER (PARTITION BY userId, itemName ORDER BY itemValue)
FROM #testTable
),
multiplied AS (
SELECT
r.userId,
r.itemName,
r.itemValue,
rn03 = r03.rn,
rn06 = r06.rn
FROM ranked r03
INNER JOIN ranked r06 ON r03.userId = r06.userId AND r06.itemName = 'q06'
INNER JOIN ranked r ON r03.userId = r.userId AND (
r.itemName = 'q03' AND r.rn = r03.rn OR
r.itemName = 'q06' AND r.rn = r06.rn OR
r.itemName NOT IN ('q03', 'q06')
)
WHERE r03.itemName = 'q03'
AND r.itemName IN ('q02', 'q03', 'q05', 'q06')
)
SELECT userId, rn03, rn06, q02, q03, q05, q06
FROM multiplied
PIVOT (
MIN(itemValue)
FOR itemName in (q02, q03, q05, q06)
) AS PivotTable
; WITH SRData AS (
SELECT -- Only query single response items in this block
[userId]
, [q01]
, [q02]
, [q04]
, [q05]
FROM
#testTable
PIVOT
(
MIN(itemValue)
FOR itemName in ([q01], [q02], [q04], [q05])
) AS PivotTable
)
SELECT
sr.[userId]
, sr.[q01]
, sr.[q02]
, [q03] = mr03.[itemValue]
, sr.[q04]
, sr.[q05]
, [q06] = mr06.[itemValue]
FROM
SRData sr
LEFT JOIN #testTable mr03 ON mr03.userId = sr.userId AND mr03.itemName = 'q03' -- Muli Response for q03
LEFT JOIN #testTable mr06 ON mr06.userId = sr.userId AND mr06.itemName = 'q06' -- Muli Response for q06
Not clear what the desired results should look like exactly but one possibility
; WITH Data AS (
SELECT
ROW_NUMBER() OVER (PARTITION BY [userId], [itemName]
ORDER BY [itemValue]) AS RN
, [userId]
, [itemName]
, [itemValue]
FROM
#testTable
)
SELECT
[userId]
, [q02]
, [q03]
, [q05]
FROM
Data
PIVOT
(
MIN(itemValue)
FOR itemName in ([q02], [q03], [q05])
) AS PivotTable
Returns
userId q02 q03 q05
----------- ------------------------------ ------------------------------ ------------------------------
1 1-q02 Answer 1-q03 Answer 1 1-q05 Answer
1 NULL 1-q03 Answer 2 NULL
1 NULL 1-q03 Answer 3 NULL
2 2-q02 Answer 2-q03 Answer 1 2-q05 Answer
2 NULL 2-q03 Answer 2 NULL

Finding matches in multiple columns in different orders

I am trying to merge 2 databases with the same schema together, and this is one part of it.
I have changed the subject to keep it more understandable - I cannot change this schema, it's just what I'm working with.
I have a table in both my source and target databases with the following columns:
Car
CarType1
CarType2
CarType3
CarType4
I am trying to write a query that will tell me in the target database, which rows have the same Cars between the 2 databases, but different CarTypes. All I need is a count on the rows that are different.
My query written in english would be:
Bring me back a count of rows where the Car is the same and the CarTypes between the two systems do not match. It doesn't matter if the CarType is in a different CarType field between the two, just whether all of the values are contained in one of the 4 fields or not.
So if in my source database this row:
Car: Mustang
CarType1: Fast
CarType2: Convertible
CarType3: null
CarType4: null
And in my target database I have this row:
Car: Mustang
CarType1: Fast
CarType2: Convertible
CarType3: Sports
CarType4: null
This would count as a non-match, since it's a Mustang and because the aggregate of the CarType fields is different. What order the values are in does not matter for this.
How would I write this query? I cannot get a grasp on it.
;WITH SourceT AS (
SELECT 'Toyota' AS Car, 'A' AS CarType1, 'B' AS CarType2, 'C' CarType3, 'D' CarType4 UNION ALL
SELECT 'BMW' AS Car, 'A' AS CarType1, 'B' AS CarType2, 'C' CarType3, 'D' CarType4 UNION ALL
SELECT 'Mustang' AS Car, 'Fast' AS CarType1, 'Convertible' AS CarType2, 'Sports' CarType3, NULL CarType4
),
TargetT AS (
SELECT 'Toyota' AS Car, 'D' AS CarType1, 'C' AS CarType2, 'B' CarType3, 'A' CarType4 UNION ALL
SELECT 'BMW' AS Car, 'D' AS CarType1, 'C' AS CarType2, 'B' CarType3, 'A' CarType4 UNION ALL
SELECT 'Mustang' AS Car, 'Fast' AS CarType1, 'Convertible' AS CarType2, NULL CarType3, NULL CarType4 )
SELECT *
FROM SourceT s
WHERE NOT EXISTS
(
SELECT *
FROM TargetT t
WHERE s.Car = t.Car AND 0 =
(SELECT COUNT(*) FROM
( (
(SELECT s.CarType1 AS t UNION ALL
SELECT s.CarType2 AS t UNION ALL
SELECT s.CarType3 AS t UNION ALL
SELECT s.CarType4 AS t )
EXCEPT
(SELECT t.CarType1 AS t UNION ALL
SELECT t.CarType2 AS t UNION ALL
SELECT t.CarType3 AS t UNION ALL
SELECT t.CarType4 AS t )
)
UNION ALL
(
(SELECT t.CarType1 AS t UNION ALL
SELECT t.CarType2 AS t UNION ALL
SELECT t.CarType3 AS t UNION ALL
SELECT t.CarType4 AS t )
EXCEPT
(SELECT s.CarType1 AS t UNION ALL
SELECT s.CarType2 AS t UNION ALL
SELECT s.CarType3 AS t UNION ALL
SELECT s.CarType4 AS t )
)
) T
)
)
Or a slightly shorter version
SELECT *
FROM SourceT s
WHERE NOT EXISTS
(
SELECT *
FROM TargetT t
WHERE s.Car = t.Car AND
(SELECT t FROM (SELECT s.CarType1 AS t UNION ALL
SELECT s.CarType2 AS t UNION ALL
SELECT s.CarType3 AS t UNION ALL
SELECT s.CarType4 AS t ) D ORDER BY t FOR XML PATH(''))=
(SELECT t FROM (SELECT t.CarType1 AS t UNION ALL
SELECT t.CarType2 AS t UNION ALL
SELECT t.CarType3 AS t UNION ALL
SELECT t.CarType4 AS t ) D ORDER BY t FOR XML PATH(''))
)
Try this and let me know if it works:
SELECT * FROM db1.dbo.Cars
EXCEPT
SELECT c1.* FROM db1.dbo.Cars as c1
INNER JOIN db2.dbo.Cars as c2
ON c1.Car = c2.Car
AND
--Check carType1
(c1.CarType1 = c2.CarType1 OR
c1.CarType1 = c2.CarType2 OR
c1.CarType1 = c2.CarType3 OR
c1.CarType1 = c2.CarType4)
AND
--Check carType2
(c1.CarType2 = c2.CarType1 OR
c1.CarType2 = c2.CarType2 OR
c1.CarType2 = c2.CarType3 OR
c1.CarType2 = c2.CarType4)
AND
--Check carType3
(c1.CarType3 = c2.CarType1 OR
c1.CarType3 = c2.CarType2 OR
c1.CarType3 = c2.CarType3 OR
c1.CarType3 = c2.CarType4)
AND
--Check carType4
(c1.CarType4 = c2.CarType1 OR
c1.CarType4 = c2.CarType2 OR
c1.CarType4 = c2.CarType3 OR
c1.CarType4 = c2.CarType4)
NOTE:
You will have to add ISNULLs to this to either include or exclude if a column is null.
To exclude:
ISNULL(c1.CarType4, -1) = ISNULL(c2.CarType4, -2)
To include:
ISNULL(c1.CarType4, -1) = ISNULL(c2.CarType4, -1)
SELECT c1.car
FROM target.cars c1
INNER JOIN source.cars c2
ON c2.car = c1.car
WHERE
COALESCE( c1.cartype1, '') NOT IN
( '', c2.cartype1, c2.cartype2, c2.cartype3, c2.cartype4 )
OR
COALESCE( c1.cartype2, '') NOT IN
( '', c2.cartype1, c2.cartype2, c2.cartype3, c2.cartype4 )
OR
COALESCE( c1.cartype3, '') NOT IN
( '', c2.cartype1, c2.cartype2, c2.cartype3, c2.cartype4 )
OR
COALESCE( c1.cartype4, '') NOT IN
( '', c2.cartype1, c2.cartype2, c2.cartype3, c2.cartype4 )
OR
LEN( COALESCE( c1.cartype1, '') + COALESCE( c1.cartype2, '') +
COALESCE( c1.cartype3, '') + COALESCE( c1.cartype4, '') )
<>
LEN( COALESCE( c2.cartype1, '') + COALESCE( c2.cartype2, '') + COALESCE( c2.cartype3, '') +
COALESCE( c2.cartype4, '') )
;