ISNULL/COALESCE for multiple fields - sql-server

Consider the following data model:
Customers
CustNum | First Name | Last Name
555 John Doe
CustomerAddresses
CustNum | ShippingAddress| Line1 | Line2 | City | State | Zip
555 | ADD1 | 333 A Dr. | Apt. 10 | Dallas | TX | 11345
555 | ADD2 | 111 B St. | NULL | Miami | FL | 22222
555 | WXYZ | 123 Main St. | NULL | Detroit | MI | 99998
OrderHeader
OrdNum | CustNum | OrderTotal | Line1 | Line2 | City | State| Zip
1000 | 555 | 67.00 | 123 Main St. | Ste 1 | Detroit | MI | 99998
OrderLine
OrderNo | Item | Price | ShippingAddress
1000 | X123 | 32.00 | ADD1
1000 | Y234 | 25.00 | ADD2
1000 | ZZZZ | 10.00 | NULL
There is a one-to-many relationship between Customers and CustomerAddresses.
Each OrderHeader, instead of a key relationship to the CustomerAddresses table, stores the address used for shipping in the Line1, Line2, City, State, and Zip fields.
In addition, it's possible to select a shipping address in the OrderLine table that overrides the address stored in the OrderHeader.
I'm trying to come up with a query to return data in the following format, to generate a list of mailing labels:
MailingLabels
OrderNo | Item | Line1 | Line2 | City | State | Zip
1000 | X123 | 333 A Dr. | Apt. 10 | Dallas | TX | 11345
1000 | Y234 | 111 B St. | NULL | Miami | FL | 22222
1000 | ZZZZ | 123 Main St. | NULL | Detroit | MI | 99998
Basically, if the OrderLine record has a ShippingAddress value, I want to return the corresponding address from the CustomerAddresses table.
If it is NULL, then I want to return the Line1, Line2, City, State, and Zip values stored in the OrderHeader table.
The problem is, when I use COALESCE or ISNULL, it's possible to return incorrect results. Here's my query:
SELECT OH.OrderNo, Item, ISNULL(CA.Line1, OH.Line1), ISNULL(CA.Line2, OH.Line2),
ISNULL(CA.City, OH.City), ISNULL(CA.State, OH.State), ISNULL(CA.Zip, OH.Zip)
FROM OrderHeader OH
JOIN OrderLine OL
ON OH.OrderNo = OL.OrderNo
LEFT JOIN CustomerAddress CA
ON OL.CustNum = CA.CustNum
AND OL.ShippingAddress = CA.ShippingAddress
With the above query, if the Line2 field is defined for the OrderHeader, but the ShippingAddress is defined in OrderLine, it's possible to return a mixed address for the Y234 item:
OrderNo | Item | Line1 | Line2 | City | State | Zip
1000 | Y234 | 111 B St. | Ste 1 | Miami | FL | 22222
Note, Ste 1 is not part of the address denoted in the OrderLine, it's actually part of the OrderHeader.
How can I write a query to return the data in the desired fashion? Any and all help is greatly appreciated!

Unfortunately, I can't think of a neat way to do this without being quite repetitive.
Assuming the OL aliases should have been CA:
SELECT OH.OrderNo, Item,
CASE WHEN OL.ShippingAddress IS NULL THEN OH.Line1 ELSE CA.Line1 END,
CASE WHEN OL.ShippingAddress IS NULL THEN OH.Line2 ELSE CA.Line2 END,
CASE WHEN OL.ShippingAddress IS NULL THEN OH.City ELSE CA.City END,
CASE WHEN OL.ShippingAddress IS NULL THEN OH.State ELSE CA.State END,
CASE WHEN OL.ShippingAddress IS NULL THEN OH.Zip ELSE CA.Zip END
FROM OrderHeader OH
JOIN OrderLine OL
ON OH.OrderNo = OL.OrderNo
LEFT JOIN CustomerAddress CA
ON OL.CustNum = CA.CustNum
AND OL.ShippingAddress = CA.ShippingAddress

Related

T-SQL Query comparing Member counts between 2 tables

TABLE 1: Data sent to vendor
| MemberID | FirstName | LastName | Etc |
| :------: | :-------: | :------: | :-: |
| 1 | John | Smith | Etc |
| 2 | Jane | Doe | Etc |
| 3 | Dan | Laren | Etc |
TABLE 2: Data returned from vendor
| MemberID | FirstName | LastName | Etc |
| :------: | :-------: | :------: | :-: |
| 1 | John | Smith | Etc |
| 2 | Jane | Doe | Etc |
| 3 | Dan | Laren | Etc |
We send data to a vendor which is used for their matching algorithm and they return the data with new information. The members are matched with a MemberID data element. How would I write a query which shows me which MemberIDs we sent to the vendor but the vendor didn't return?
NOT EXITS would be my first choice here.
Example
SELECT *
FROM Table1 A
WHERE NOT EXISTS (SELECT 1
FROM Table2 B
WHERE A.MemberID = B.MemberID )
SELECT MemberID
FROM Table1
WHERE MemberID NOT IN (SELECT MemberID FROM Table2)
Using EXCEPT is one option.
SELECT sent.[MemberID] FROM Tbl1_SentToVendor sent
EXCEPT
SELECT recv.[MemberID] FROM Tbl2_ReturnedFromVendor recv
This is just on MemberID, but the "EXCEPT" syntax can also support additional columns (e.g., in case you want to filter out data that may be the same as what you already have.)

SQL Server Case Statement- Deriving one column from two others when grouping by transactions

I am looking to apply make a flag based on the below scenario. The source table and desired results I am after are in the tables below.
When SRCE_SYST_FLAG isn't null (and = Y), then create a new flag (DERIVED_FLAG) using the value in the SRCE_SYST_FLAG column, else use the GENERIC_FLAG column.
However if another record in the same TRAN_ID group has the SRCE_SYST_FLAG column populated, set the DERIVED_FLAG to the other SRCE_SYST_F. This is the case in TRAN_ID = 2222 below.
Would anyone know how to do this using a case statement? I can make a generic one such as the one below, but this doesn't use the SRCE_SYST_FLAG for the records that are NULL but the SRCE_SYST_FLAG is populated for another record in that tran_id group :
CASE
WHEN SRCE_SYST_Flag = 'Y'
THEN SRCE_SYST_FLAG
ELSE GENERIC_FLAG
END AS 'DERIVED_FLAG'
Sample data set:
Client | Limit | Pdct_type_c | SRCE_SYST_FLAG | GENERIC_FLAG | TRAN_ID
--------------------------------------------------------------------------------
John | 4,000,000.00 | DCO | y | N | 2222
John | | DCO | NULL | N | 2222
Mark | 2,000,000.00 | DCO | NULL | N | 435
Mark | | DCO | NULL | N | 435
Luke | 4,000,000.00 | DCO | N | Y | 980
Desired result:
Client | Limit | Pdct_type_c | DERIVED_FLAG | TRAN_ID
---------------------------------------------------------------
John | 4,000,000.00 | DCO | Y | 2222
John | | DCO | Y | 2222
Mark | 2,000,000.00 | DCO | N | 435
Mark | | DCO | N | 435
Luke | 4,000,000.00 | DCO | Y | 980
I appreciate any assistance,
Cheers
Use SUM OVER to count the number of 'Y' per tran_id:
select client
, limit
, Pdct_type_c
, TRAN_ID
, case when SUM(case when SRCE_SYST_FLAG = 'Y' then 1 else 0 end) OVER (partition by tran_id) > 0 then 'Y' else GENERIC_FLAG end DERIVED_FLAG
from table

Return a single row when joining table has many values

Looked around for a bit but can't find anything that resembles what I need. I have two tables that are joined on ID and the second table has multiple details about the first under different properties using the same ID. So ID 1 has properties like A, B, C etc and there are 9 of them total for each ID. I would like to return a single row per ID that has all the different properties.
Here are my tables.
MAIN_Table
ID | Name
------ | ------
100 | Frank
200 | Bill
300 | Anne
Property_Table
ID | Prop | Value
------ | ------| -----
100 | AA | $4
100 | BB | ER
200 | BB | AMB
300 | AA | $10
200 | AA | $5
300 | BB | ER
I have tried several case statements which seem to return a row for each listed value instead of on a single line, i don't like/want this...
ID | Name | Prop_AA | Prop_BB
----- | ------ | ------ | -------
100 | Frank | $4 | NULL
100 | Frank | NULL | ER
200 | Bill | $5 | NULL
200 | Bill | NULL | AMB
300 | Anne | $10 | NULL
300 | Anne | NULL | ER
I would like everything to be on a single line per ID like this...
ID | Name | Prop AA | Prop BB
----- | ------ | ------ | -------
100 | Frank | $4 | ER
200 | Bill | $5 | AMB
300 | Anne | $10 | ER
I used 2 common table expressions to get the results you were needing. Common_Table_Expression
--CTE to get Prop AA's Id & Value
WITH Prop_AA AS
(
SELECT ID,Value
FROM Property_Table
WHERE Prop = 'AA'
),
--CTE to get Prop BB's Id & Value
Prop_BB AS
(
SELECT ID,Value
FROM Property_Table
WHERE Prop = 'BB'
)
SELECT mt.ID,Name,aa.Value as 'Prop AA',bb.Value as 'Prop BB'
FROM MAIN_Table mt
JOIN Prop_AA aa ON mt.ID = aa.ID -- Join Prop_AA
JOIN Prop_BB bb ON mt.ID = bb.ID -- Join Prop_BB
ORDER BY mt.ID

Exclude Secondary ID Records from Original SELECT

I'm relatively new to SQL and am running into a lot of issues trying to figure this one out. I've tried using a LEFT JOIN, and dabbled in using functions to get this to work but to no avail.
For every UserID, if there is a NULL value, I need to remove all records of the Product ID for that UserID from my SELECT.
I am using SQL Server 2014.
Example Table
+--------------+-------------+---------------+
| UserID | ProductID | DateTermed |
+--------------+-------------+---------------+
| 578 | 2 | 1/7/2017 |
| 578 | 2 | 1/7/2017 |
| 578 | 1 | 1/15/2017 |
| 578 | 1 | NULL |
| 649 | 1 | 1/9/2017 |
| 649 | 2 | 1/11/2017 |
+--------------+-------------+---------------+
Desired Output
+--------------+-------------+---------------+
| UserID | ProductID | DateTermed |
+--------------+-------------+---------------+
| 578 | 2 | 1/7/2017 |
| 578 | 2 | 1/7/2017 |
| 649 | 1 | 1/9/2017 |
| 649 | 2 | 1/11/2017 |
+--------------+-------------+---------------+
Try the following:
SELECT a.userid, a.productid, a.datetermed
FROM yourtable a
LEFT OUTER JOIN (SELECT userid, productid, datetermed FROM yourtable WHERE
datetermed is null) b
on a.userid = b.userid and a.productid = b.productid
WHERE b.userid is not null
This will left outer join all records with a null date to their corresponding UserID and ProductID records. If you only take records that don't have an associated UserID and ProductID in the joined table, you should only be left with records that don't have a null date.
You can use this WHERE condition:
SELECT
UserID,ProducID,DateTermed
FROM
[YourTableName]
WHERE
(CONVERT(VARCHAR,UserId)+
CONVERT(VARCHAR,ProductID) NOT IN (
select CONVERT(VARCHAR,UserId)+ CONVERT(VARCHAR,ProductID)
from
[YourTableName]
where DateTermed is null)
)
When you concatenate the UserId and the ProductId get a unique value for each pair, then you can use them as a "key" to exclude the "pairs" that have the null value in the DateTermed field.
Hope this help.

sql server join 3 tables, not in other column of third table

So, i have two tables which are connected by a third one.
Something like:
First Table:
+------------+--------------+--------------+------------+
| box_column | code_column | from_column | to_column |
+------------+--------------+--------------+------------+
| 12345 | sdsad1 | madrid | london |
+------------+--------------+--------------+------------+
Second Table:
+-------------+--------------+
| code_column | truck_column |
+-------------+--------------+
| sdsad1 | truck1 |
| sdsad1 | truck2 |
| sdsad1 | truck3 |
+-------------+--------------+
Third Table:
+--------------+-------------+-----------+
| truck_column | from_column | to_column |
+--------------+-------------+-----------+
| truck1 | madrid | paris |
| truck2 | paris | calais |
| truck3 | calais | london |
+--------------+-------------+-----------+
after having a join, just having the number of the box, is there any way i can distinct the trucks that make the last leg (third table have london in the to_column) and the others?
That is not very difficult to do, it's actually quite basic:
SELECT *
FROM dbo.code c
INNER JOIN dbo.jointable j on c.code = j.code
INNER JOIN dbo.truck t on j.truck = t.truck
WHERE c.box = 12345
AND c.[to] = t.[to]
Where code is your first table, jointable is your second table, and truck is your third table.
The output of this query is:
box code from to code truck truck from to
---------------------------------------------------------------------
12345 sdsad1 madrid london sdsad1 truck3 truck3 calais london
To get only the truck as output, replace
SELECT *
with
SELECT t.truck
Last but not least: I'm not seeing any primary keys, nor foreign keys in your model. Maybe you left it out. If not, please use keys and constraints.

Resources