Select sub queries within a select - sql

insert into time_test(Difference) values
(select ((select actual from time_test where id = :p13_id) -
(select
(monday+tuesday+wednesday+thursday+friday) from time_test where id=
:p13_id
)) from time_test where id= :p13_id)
Difference is a column in time_test which is null, and :p13_id is a page item for Oracle Apex.
I know I need to wrap it in nvl or some function like that but I don't know how.

It looks like you're actually trying to do an update, not an insert:
update time_test
set difference = actual - (monday+tuesday+wednesday+thursday+friday)
where id = :p13_id
If any of the 'day' columns might be null then you can use nvl() or coalesce() to default them to zero so they don't break the calculation:
update time_test
set difference = actual - coalesce(monday, 0) - coalesce(tuesday, 0)
- coalesce(wednesday, 0) - coalesce(thursday, 0) - coalesce(friday, 0)
where id = :p13_id
You could also do coalesce(actual, 0) but it might make more sense to leave the difference null if that is not set. It depends what you want to see in that case.
In this case the nvl() and coalesce() functions are equivalent. If the first argument - e.g. monday - is null then the second argument is substituted. So nvl(monday, 0) will give you the actual value of monday if it is not null, but will give you zero if it is null. You will get the same effect from coalesce(), but that allows a list of multiple expressions to be evaluated and will return the first non-null value from the list.
Another approach to this is to make difference a virtual column that is calculated on the fly, or calculate it in a view over the table; either would remove the duplicate data storage and the need to maintain the value yourself. And if you did definitely want a physical column you could set it from a trigger so automate the maintenance in case any of the other columns are updated outside your Apex application. But a virtual column is probably simpler and neater.

Related

Replace empty date values with a matching value from the same column?

I have a small query that looks like this:
SELECT
CS.ID,
CS.Import_Date,
CS.Secondary_Date
FROM
Center_Summary CS
ORDER BY CS.Import_Date
Which returns values like this:
And I want to replace these "empty" values which are pulling as 01/01/1900 with the value of 05/01/2019. In this case, it's because the ID and Import_Date match, so the Secondary_Date should match as well. I've thought to use REPLACE() (REPLACE(CS.Secondary_Date, '01/01/1900', ???), but I'm not sure how to write logic to pull in a matching value from the column Secondary_Date based on ID and Import_Date - what function should I be looking to use here?
How it's currently pulling (the dates in red I want to replace):
What my expected result is:
Why not just use a UPDATE with a WHERE?
UPDATE dbo.YourTable
SET SecondaryDate = ImportDate
WHERE SecondaryDate = '19000101';
"Empty" values are best represented by NULL. I would recommend converting them to NULL:
nullif(secondary_date, '1900-01-01')
If you really want another value, then you can use coalesce() or a case expression:
coalesce(nullif(secondary_date, '1900-01-01'), '2019-05-01')
However, I'm not generally a fan of such magic values in the code.

Average Row [SQL]

Actually I'm a bit confused about what should i wrote in the subject.
The point is like this, I want to average the Speed01,Speed02,Speed03 and Speed04 :
SELECT
Table01.Test_No,
Table01.Speed01,
Table01.Speed02,
Table01.Speed03,
Table01.Speed04,
I want to create new column that consists of this average -->>
AVG(Table01.Speed01, Table01.Speed02, Table01.Speed03,Table01.Speed04) as "Average"
I have tried this, but it did not work.
From
Table01
So, the contain of the Speed column could be exist but sometimes the Speed02 don't have number but the others are have numbers. sometimes speed04 data is also missing and the others is exist, sometimes only one data (example: only Speed01) have the data. lets say it depends on the sensor ability to catch the speed of the test material.
It will be a big help if you can find the solution. I'm newbie here.
THANK YOU ^^
AVG is a SQL aggregate function, therefore not applicable. So simply do the math. Average is sum divided by count:
(SPEED01 + SPEED02 + SPEED03 +SPEED04)/4
To deal with missing values, use NULLIF or COALESCE:
(COALESCE(SPEED01, 0) + COALESCE(SPEED02, 0) + COALESCE(SPEED03, 0) + COALESCE(SPEED04, 0))
That leaves the denominator. You need to add 1 for every non null. For example:
(COALESCE(SPEED01/SPEED01,0) + COALESCE(SPEED02/SPEED02,0) + ...)
You can also use CASE, depending on the supported SQL dialect, to avoid the possible divide by 0:
CASE WHEN SPEED01 IS NULL THEN 0 ELSE 1
OR you can normalize the data, extract all SPEEDs into a 1:M relation and use the AVG aggregate, avoiding all these issues. Not to mention the possibility to add a 5th measurement, then a 6th and so on and so forth!
Just add the columns and divide them by 4. To deal with the "missing" values use coalesce to treat NULL values as zero:
SELECT Test_No,
(coalesce(Speed01,0) + coalesce(Speed02,0) + coalesce(Speed03,0) + coalesce(Speed04,0)) / 4 as "Average"
FROM Table01;
You didn't mention your DBMS (Postgres, Oracle, ...), but the above is ANSI (standard) SQL and should run on nearly every DBMS.
As I understood your question, I supposed that Table01.Speed01, Table01.Speed03, Table01.Speed04 are nullable and of type int whereas Table01.Speed02 is nullable and of type nvarchar:
SELECT
Table01.Test_No,
(
ISNULL(Table01.Speed01, 0) +
CASE ISNUMERIC(Table01.Speed02) WHEN 0 THEN 0 ELSE CAST(Table01.Speed02 AS int) END +
ISNULL(Table01.Speed03, 0) +
ISNULL(Table01.Speed04, 0)
)/4 AS AVG
FROM Table01

Problems with Postgresql CASE syntax

Here is my SQL query:
SELECT (CASE (elapsed_time_from_first_login IS NULL)
WHEN true THEN 0
ELSE elapsed_time_from_first_login END)
FROM (
SELECT (now()::ABSTIME::INT4 - min(AcctStartTime)::ABSTIME::INT4)
FROM radacct
WHERE UserName = 'test156') AS elapsed_time_from_first_login;
When I execute the above query, I get this error:
ERROR: CASE types record and integer cannot be matched
From the error message I understand that PostgreSQL take the second select, respectively elapsed_time_from_first_login as a row, even if it will always be a single value (because of the min() function).
Question: do you have some suggestions on how to deal with this query?
I suppose, what you are actually trying to do should look like this:
SELECT COALESCE((SELECT now() - min(acct_start_time)
FROM radacct
WHERE user_name = 'test156')
, interval '0s')
While there is an aggregate function in the top SELECT list of the subselect, it cannot return "no row". The aggregate function min() converts "no row" to NULL, and the simple form below also does the trick.
db<>fiddle here
Oldsqlfiddle
Other problems with your query have already been pointed out. But this is the much simpler solution. It returns an interval rather than an integer.
Convert to integer
Simplified with input from artaxerxe.
Simple form does the job without check for "no row":
SELECT COALESCE(EXTRACT(epoch FROM now() - min(acct_start_time))::int, 0)
FROM radacct
WHERE user_name = 'test156';
Details about EXTRACT(epoch FROM INTERVAL) in the manual.
Aggregate functions and NULL
If you had used the aggregate function count() instead of sum() as you had initially, the outcome would be different. count() is a special case among standard aggregate functions in that it never returns NULL. If no value (or row) is found, it returns 0 instead.
The manual on aggregate functions:
It should be noted that except for count, these functions return a
null value when no rows are selected. In particular, sum of no rows
returns null, not zero as one might expect, and array_agg returns
null rather than an empty array when there are no input rows. The
coalesce function can be used to substitute zero or an empty array for
null when necessary.
Postgres is complaining that 0 and elapsed_time_from_first_login are not the same type.
Try this (also simplifying your select):
select
coalesce(elapsed_time_from_first_login::INT4, 0)
from ...
Here is how I formatted the SQL and now is working:
SELECT coalesce(result, 0)
FROM (SELECT (now()::ABSTIME::INT4 - min(AcctStartTime)::ABSTIME::INT4) as result
FROM radacct WHERE UserName = 'test156') as elapsed_time_from_first_login;
The second SELECT is returning a table, named elapsed_time_from_first_login with one column and one row. You have to alias that column and use it in the CASE clause. You can't put a whole table (even if it is one column, one row only) where a value is expected.
SELECT (CASE (elapsed_time IS NULL)
WHEN true THEN 0
ELSE elapsed_time end)
FROM (SELECT (now()::ABSTIME::INT4 - min(AcctStartTime)::ABSTIME::INT4)
AS elapsed_time -- column alias
FROM radacct
WHERE UserName = 'test156'
) as elapsed_time_from_first_login; -- table alias
and you can shorten the CASE by using the COALESCE() function (and optionally add an alias for that column to be shown in the results):
SELECT COALESCE(elapsed_time, 0)
AS elapsed_time
FROM (SELECT (now()::ABSTIME::INT4 - min(AcctStartTime)::ABSTIME::INT4)
AS elapsed_time
FROM radacct
WHERE UserName = 'test156'
) as elapsed_time_from_first_login; -- table alias

how can I replace blank value with zero in MS-Acess

I have below query in Ms-Access but I want to replace Blank value with zero but I can't get proper answer. Is there any way to replace blank value in zero.
(SELECT
SUM(IIF(Review.TotalPrincipalPayments,0,Review.TotalPrincipalPayments))+
SUM(IIF(Review.TotalInterestPayments,0,Review.TotalInterestPayments ))
FROM
tblReviewScalars as Review
INNER JOIN tblReportVectors AS Report ON(Review.LoanID=Report.LoanID)
WHERE Report.AP_Indicator="A" AND Report.CashFlowDate=#6/5/2011# AND Review.AsofDate=#6/5/2011# AND ( Review.CreditRating =ReviewMain.CreditRating)) AS [Cash Collected During the Period],
I assume TotalPrincipalPayments and TotalInterestPayments are both numeric types, hence the 'blanks' in question is the NULL value.
In SQL, the set function SUM will disregard NULL values, unless all values resolve to NULL in which case NULL is returned (erroneously and the error is with SQL not Access for a change :)
To use a simple example, SELECT SUM(a) FROM T; will only return NULL when a IS NULL is TRUE for all rows of T or when T is empty. Therefore, you can move the 'replace NULL with zero' logic outside of the SUM() function. Noting that "NULLs propagate" in calculations, you will need to handle NULL for each SUM().
You haven't posted the whole of your query e.g. the source of the correlation name ('table alias') ReviewMain is not showm. But it seems clear you are constructing a derived table named "Cash Collected During the Period", in which case your calculated column needs an AS clause ('column alias') such as TotalPayments e.g.
...
(
SELECT IIF(SUM(Review.TotalPrincipalPayments) IS NULL, 0, SUM(Review.TotalPrincipalPayments))
+ IIF(SUM(Review.TotalInterestPayments) IS NULL, 0, SUM(Review.TotalInterestPayments))
AS TotalPayments
FROM tblReviewScalars as Review
INNER JOIN tblReportVectors AS Report
ON Review.LoanID = Report.LoanID
WHERE Report.AP_Indicator = 'A'
AND Report.CashFlowDate = #2011-05-06#
AND Review.AsofDate = #2011-05-06#
AND Review.CreditRating = ReviewMain.CreditRating
) AS [Cash Collected During the Period], ...
An alternative to #onedaywhen's answer is to use the nz function, which is specifically for null-substitution:
SELECT
SUM(NZ(Review.TotalPrincipalPayments,0))+
SUM(NZ(Review.TotalInterestPayments,0))
...
As onedaywhen pointed out, this is functionally equivalent to putting the function outside the aggregate, which may perform better (the function is called once, rather than once per un-aggregated row):
SELECT
NZ(SUM(Review.TotalPrincipalPayments),0)+
NZ(SUM(Review.TotalInterestPayments),0)
...
To change a null value to a zero in an Access 2010 database, open your table, go to design view, click on the field and set the default value to: =0.

What does this SQL Query mean?

I have the following SQL query:
select AuditStatusId
from dbo.ABC_AuditStatus
where coalesce(AuditFrequency, 0) <> 0
I'm struggling a bit to understand it. It looks pretty simple, and I know what the coalesce operator does (more or less), but dont' seem to get the MEANING.
Without knowing anymore information except the query above, what do you think it means?
select AuditStatusId
from dbo.ABC_AuditStatus
where AuditFrequency <> 0 and AuditFrequency is not null
Note that the use of Coalesce means that it will not be possible to use an index properly to satisfy this query.
COALESCE is the ANSI standard function to deal with NULL values, by returning the first non-NULL value based on the comma delimited list. This:
WHERE COALESCE(AuditFrequency, 0) != 0
..means that if the AuditFrequency column is NULL, convert the value to be zero instead. Otherwise, the AuditFrequency value is returned.
Since the comparison is to not return rows where the AuditFrequency column value is zero, rows where AuditFrequency is NULL will also be ignored by the query.
It looks like it's designed to detect a null AuditFrequency as zero and thus hide those rows.
From what I can see, it checks for fields that aren't 0 or null.
I think it is more accurately described by this:
select AuditStatusId
from dbo.ABC_AuditStatus
where (AuditFrequency IS NOT NULL AND AuditFrequency != 0) OR 0 != 0
I'll admit the last part will never do anything and maybe i'm just being pedantic but to me this more accurately describes your query.
The idea is that it is desireable to express a single search condition using a single expression but it's merely style, a question of taste:
One expression:
WHERE age = COALESCE(#parameter_value, age);
Two expressions:
WHERE (
age = #parameter_value
OR
#parameter_value IS NULL
);
Here's another example:
One expression:
WHERE age BETWEEN 18 AND 65;
Two expressions
WHERE (
age >= 18
AND
age <= 65
);
Personally, I have a strong personal perference for single expressions and find them easier to read... if I am familiar with the pattern used ;) Whether they perform differently is another matter...