PostgreSQL case statement for mutliple date fields - sql

I'm presenting a query wherein I want to pull only the earliest date from a table that has two date fields. It is possible that both date fields are null. However, I only want to pull values if at least one of the fields is not null. Also, if the first date field is not null, then I want that value. In other words, I prefer the first date field.
Here is what I've come up with, but I don't feel 100% this is correct. And, I'm also thinking there's a better way to skin the proverbial cat.
Select
o.order_num,
Case
when ro.pay1_date is not null then ro.pay1_date
when ro.pay2_date is not null then ro.pay2_date
End as funding_date
From orders o
left join recon_order ro on(ro.order_id = o.order_id)
Where (ro.pay1_date is not null or ro.pay2_date is not null)
Order by funding_date desc
Limit 500
I first ran this query without the Where clause, but that just returned 500 Null values, which I never completely understood.
And as a side note I'm working with an older version of PostgreSQL 8.1 (not my choice). But just saying this because perhaps the old version won't support certain new query syntax.
Thanks in advance for any input that corrects or improves this!

You can use a least to get the least of the 2 dates. When both of them are null, it returns null. You can filter the null rows later.
SELECT *
FROM
(SELECT o.order_num,
least(ro.pay1_date,ro.pay2_date) as funding_date
FROM orders o
LEFT JOIN recon_order ro ON ro.order_id = o.order_id) x
WHERE funding_date IS NOT NULL

Related

Question on getting number of day from single date column

Above is the screenshot of the tables for my practice. I want to extract the number of days between the earliest and latest sales made by staff 'Ali'. I do not have any SQL IDE to run the code and want to check any problem with my code.
SELECT DAYDIFF(day, MAX(st.Date), MIN(st.Date)) AS Duration
FROM SALES_TRANSACTION AS ST
LEFT JOIN SALES_MASTER AS sm
ON sm.Product_ID = st.Product_ID
GROUP BY sm.Staff_Name
HAVING sm.Staff_Name = 'Ali'
ORDER BY st.Date DESC
Here is the dataset
https://drive.google.com/file/d/13XCxQgbEONU22ZDYhQq-I1u-dh3A2fPc/view?usp=sharing
You want logic more like this:
SELECT DAYDIFF(day, MIN(st.Date), MAX(st.Date)) AS Duration
FROM SALES_TRANSACTION ST JOIN
STAFF_MASTER sm
ON sm.Staff_id = st.Staff_Id
WHERE sm.Staff_Name = 'Ali';
Note the changes:
The filtering is the in WHERE clause rather than the HAVING. In general, it is better to filter before aggregating if possible.
The LEFT JOIN is replaced by a JOIN. First, you need a match to get the name. Second, the foreign key reference should be valid so an outer join should not be necessary.
The correct table for the staff name is STAFF_MASTER.
If you are using SQL Server (which has the 3 argument DATEDIFF() syntax), then the smaller date is the second argument.
And finally, there are many tools on the web where you can test SQL, such as db<>fiddle, SQL Fiddle, and db-fiddle. You can also download free databases onto almost any platform.

SQL Server 2016 AdventureWorks NULL checks

I am working on 2 different assignments where I have to do null checks but I'm not sure if I have written the syntax correctly for that my instructor has not really discussed this but will be marking for it.
Below are the 2 questions and what I have written. Any help is appreciated.
Assignment 1 question: Create a list of the sales order numbers for orders not ordered online and not with a credit card. Note: 0 is false and 1 is true for bit fields. Below is the syntax i used, am i doing a null check here?
SELECT SalesOrderNumber
FROM Sales.SalesOrder_json
WHERE OnlineOrderFlag = 0 AND CreditCardID IS NULL
Assignment 2 question: list the vendors that have no products. Below is the syntax I used, am I doing a null check here?
SELECT
pv.Name AS Vendors,
COUNT(PP.ProductID) AS 'Products'
FROM
Purchasing.Vendor AS PV
LEFT JOIN
Purchasing.ProductVendor AS PPV ON PV.BusinessEntityID = PPV.BusinessEntityID
LEFT JOIN
Production.Product AS PP ON PP.ProductID = PPV.ProductID
GROUP BY
PV.Name
HAVING
COUNT(PP.ProductID) = 0;
Welcome to Stack Overflow!
In the future, please post a summary or create table statements that represents the schema of the tables used in your queries so that we have enough information to provide more than speculative responses. Even though this is the Adventure Works DB, you should start your SO journey with good habits!
please try not to post direct Assignment questions online as you will easily get done for plagiarism by most academic assignment checkers, mainly because other students may see your post, and the support that you get from the community which could result in all of you handing in the same result.
Have you run your queries? Do you think the results are correct?
If the results from your queries are correct, then the only issue is "have you done any null checks"? One could say that if your results have returned the correct results then you must have satisfied the criteria, otherwise the question wasn't formulated very well.
Null checks can be summarised into 3 patterns:
You directly compare against null using IS NULL or IS NOT NULL in your query
Use of JOIN syntax to deal with data that may have nulls.
INNER JOIN will limit the results to only records that match in both tables. Use this if you need to omit records that have a null in the foreign key field.
Non INNER joins, like LEFT JOIN. This will return results from the left table, even if there are no matching records in the joined or right table.
This is a good discussion on all supported joins: LEFT JOIN vs. LEFT OUTER JOIN in SQL Server
Use of Aggregation functions, aggregates will generally omit null values, COUNT will return 0 if all values are NULL, where as other aggregates such as SUM, MIN, MAX, AVG will return NULL if all values are NULL
Question 1
Clearly you have implemented a NULL check because you have evaluated criteria directly on the nullable column.
It looks like your answer to Question 1 is pretty good.
Question 2
While your query looks like it would return the vendors with no products, it is also returning a count of zero.
You do not need to output a column so that you can use it in a filter criteria, so remove COUNT(PP.ProductID) AS 'Products' unless you have been otherwise instructed to use it.
Is this a NULL check... That up to the interpretation, I think in this case the answer is yes. By using LEFT JOIN (or OUTER joins) you have created a result set that will have the field PP.ProductID with a value of NULL If there are no products.
Using Count in the filter criteria over that null column and recognising that a Count with a zero result means that the ProductID column was in fact null means you have evaluated a null check.
There are other ways to query for the same results, such as using NOT EXISTS. NOT EXISTS would NOT be a direct null check, because NULLABILITY was not evaluated directly.

Setting the ID in a fact table from a dimension table

In my dimension table for abandoned calls I have the ID 1 Code NO , ID 2 Code YES
I am wanting to load these ID's into the fact table based on whether or not the call was abandoned using a join.
How ever the problem I'm having it that the Abandoned value in my database is NULL for NO and 1 for YES.
So when i join
INNER JOIN datamartend.dbo.Abandoned_Call_Dim
ON incoming_measure.Abandoned = Abandoned_Call_Dim.abandoned_code
It's pulling no results?
Any ideas around this?
Basically what is needed is:
I want the abandoned ID from the abandoned dimension to be 2 if the abandonded value in measure is null and abandoned id 2 if not null
Thanks
You can use a CASE WHEN clause to get around this (or ISNULL, but case when is more portable across different DB engines)
INNER JOIN datamartend.dbo.Abandoned_Call_Dim
ON case when incoming_measure.Abandoned is null then '0'
else incoming_measure.Abandoned end
= case when Abandoned_Call_Dim.abandoned_code is null then '0'
else Abandoned_Call_Dim.abandoned_code end
This will replace nulls with 0. As long as you don't have a 0 code, you should be fine. If you do, try -1, or some other value you know is not in the possible set of codes.
Another thing to do if you have an unknown set of codes would be to do the join and add:
OR (incoming_measure.Abandoned is null and Abandoned_Call_Dim.abandoned_code is null)
Which doesn't technically join - it cross joins the null records (and as long as there's only one null that matters on the abandoned call dim, you're fine).
Can you check whether it is possible for you to use Decode function for the ID before doing Join.
Decode(value) = joining column
or try using
COALESCE(REPLACE(COL, VAL_TO_B_REPLACE_IF_NOT_NULL), VALUE_TO_REPLCE_WHEN_NULL)

Access 2010 Query Confusion

Just need some quick clarification
I have 2 Queries in my Access Database that should return Inverse results:
SELECT Equipment.title
FROM Equipment
WHERE (((Equipment.[EquipmentID]) Not In (
select EquipmentID
from DownPeriod
where UpDate is null
)));
The 2nd just excludes the Not before the In.
My Confusion comes from the fact that the query posted above does not return any results if an EquipmentID field has at least 1 null value in the DownPeriod table.
It works fine if the fields are filled, and the inverse query list always works. This makes me think there's an issue with the null value.
Now this field should never be null but I wanted to know if I could still get this to work in the unlikely event a null did occur.
Thank you in advanced!
Try joins:
SELECT Equipment.title FROM Equipment INNER JOIN DownPeriod
ON Equipment.EquipmentID = DownPeriod.EquipmentID
WHERE DownPeriod.UpDate is null
and
SELECT Equipment.title FROM Equipment INNER JOIN DownPeriod
ON Equipment.EquipmentID = DownPeriod.EquipmentID
WHERE DownPeriod.UpDate is not null
See if a change in syntax fixes your issue.
Not only should this work, but I believe it is a faster practise than using the IN() NOT IN() methods (might be wrong on that, but it looks nicer to read). It also adds the ability to quickly change the "is not null" criteria just the same as IN->NOT IN
I agree with StuckAtWork's approach. However, if you still want to understand why your original approach didn't produce the results you want, I think I can help you.
There may be an issue with empty strings which could complicate the situation. But regardless of whether or not empty strings are involved you have something more fundamental to consider.
Here is my version of the Equipment table.
EquipmentID title
1 one
2 two
3 three
And here is my version of the DownPeriod table.
ID EquipmentID text_field
1 1 one
2 2 two
3 Null
4 3 three
I didn't include your UpDate field in my DownPeriod table. It's irrelevant to your problem.
I pasted your SQL into a new Access query, discarded the WHERE clause from the subquery, and got exactly the same result as this query --- no rows returned:
SELECT e.title
FROM Equipment AS e
WHERE
e.EquipmentID Not In (
SELECT EquipmentID
FROM DownPeriod
);
So consider this situation from the db engine's perspective. Using my version of the Downloads table, it has a set of values (1, 2, Null, and 3) from the subquery. You're asking it to show you the rows from Equipment where EquipmentID is NOT IN that list of values. The db engine will only give you the rows for which that condition is True.
Null is the problem. For each EquipmentID, when it considers whether that value is not present in the subquery set, it doesn't know. That Null is an unknown value ... and the unknown value might be the same as the current EquipmentID it's considering ... or might be something else. But since the db engine doesn't know the real value, it can't evaluate the condition as True, so will not include that row in the result set. The same thing happens for every row in Equipment table ... therefore your query's result set is empty (no rows).
You could get your desired results by excluding Null values from the subquery result set with a WHERE clause like the one below. But I think StuckAtWork's suggestion is a better way to go.
SELECT e.title
FROM Equipment AS e
WHERE
e.EquipmentID Not In (
SELECT EquipmentID
FROM DownPeriod
WHERE EquipmentID Is Not Null
);

Why does my left join in Access have fewer rows than the left table?

I have two tables in an MS Access 2010 database: TBLIndividuals and TblIndividualsUpdates. They have a lot of the same data, but the primary key may not be the same for a given person's record in both tables. So I'm doing a join between the two tables on names and birthdates to see which records correspond. I'm using a left join so that I also get rows for the people who are in TblIndividualsUpdates but not in TBLIndividuals. That way I know which records need to be added to TBLIndividuals to get it up to date.
SELECT TblIndividuals.PersonID AS OldID,
TblIndividualsUpdates.PersonID AS UpdateID
FROM TblIndividualsUpdates LEFT JOIN TblIndividuals
ON ( (TblIndividuals.FirstName = TblIndividualsUpdates.FirstName)
and (TblIndividuals.LastName = TblIndividualsUpdates.LastName)
AND (TblIndividuals.DateBorn = TblIndividualsUpdates.DateBorn
or (TblIndividuals.DateBorn is null
and (TblIndividuals.MidName is null and TblIndividualsUpdates.MidName is null
or TblIndividuals.MidName = TblIndividualsUpdates.MidName))));
TblIndividualsUpdates has 4149 rows, but the query returns only 4103 rows. There are about 50 new records in TblIndividualsUpdates, but only 4 rows in the query result where OldID is null.
If I export the data from Access to PostgreSQL and run the same query there, I get all 4149 rows.
Is this a bug in Access? Is there a difference between Access's left join semantics and PostgreSQL's? Is my database corrupted (Compact and Repair doesn't help)?
ON (
TblIndividuals.FirstName = TblIndividualsUpdates.FirstName
and
TblIndividuals.LastName = TblIndividualsUpdates.LastName
AND (
TblIndividuals.DateBorn = TblIndividualsUpdates.DateBorn
or
(
TblIndividuals.DateBorn is null
and
(
TblIndividuals.MidName is null
and TblIndividualsUpdates.MidName is null
or TblIndividuals.MidName = TblIndividualsUpdates.MidName
)
)
)
);
What I would do is systematically remove all the join conditions except the first two until you find the records drop off. Then you will know where your problem is.
This should never happen. Unless rows are being inserted/deleted in the meantime,
the query:
SELECT *
FROM a LEFT JOIN b
ON whatever ;
should never return less rows than:
SELECT *
FROM a ;
If it happens, it's a bug. Are you sure the queries are exactly like this (and you have't omitted some detail, like a WHERE clause)? Are you sure that the first returns 4149 rows and the second one 4103 rows? You could make another check by changing the * above to COUNT(*).
Drop any indexes from both tables which include those JOIN fields (FirstName, LastName, and DateBorn). Then see whether you get the expected
4,149 rows with this simplified query.
SELECT
i.PersonID AS OldID,
u.PersonID AS UpdateID
FROM
TblIndividualsUpdates AS u
LEFT JOIN TblIndividuals AS i
ON
(
(i.FirstName = u.FirstName)
AND (i.LastName = u.LastName)
AND (i.DateBorn = u.DateBorn)
);
For whatever it is worth, since this seems to be a deceitful bug and any additional information could help resolving it, I have had the same problem.
The query is too big to post here and I don't have the time to reduce it now to something suitable, but I can report what I found. In the below, all joins are left joins.
I was gradually refining and changing my query. It had a derived table in it (D). And the whole thing was made into a derived table (T) and then joined to a last table (L). In any case, at one point in its development, no field in T that originated in D participated in the join to L. It was then the problem occurred, the total number of rows mysteriously became less than the main table, which should be impossible. As soon as I again let a field from D participate (via T) in the join to L, the number increased to normal again.
It was as if the join condition to D was moved to a WHERE clause when no field in it was participating (via T) in the join to L. But I don't really know what the explanation is.