Why does Postgres select return wrong floating point calculation? - sql

Why does SELECT 2 * 4 * ( 5 / 2) * 4 return 64 instead of 80?
The real query I'm running looks like this:
SELECT 2 * equity_gains_score * (tenure_score / 2) * investor_score AS propensity
but I had the same result when I ran it substituting numbers for columns. How can I fix this statement?

Postgres does integer division. So, 5/2 is 2 rather than 2.5.
You can just add a decimal point to one of the operands of the division:
SELECT 2 * 4 * ( 5 / 2.0) * 4
Or convert a value to a numeric:
SELECT 2 * 4 * ( 5::numeric / 2.0) * 4
Note: If you want an integer as the result, then you need to convert back to an integer:
SELECT (2 * 4 * ( 5 / 2.0) * 4)::int

Related

How can I Roundoff in SQL with a sum function

In My SQL Code I am trying to round the value to 2 decimal point with sum
select ((SUM(Round((CAST(PE.GstTotal as float) * PE.Quantity) / 2 ),2))) FROM [dbo].[PharmacyEntry] PE
But I am getting an error. Could someone correct me on this.
Error
It's sometimes helpful to vertically align all your parenthesis pairs to see where you've got one wrong:
select
(
(
SUM
(
Round
(
(
CAST
(
PE.GstTotal as float
)
*
PE.Quantity
)
/
2
),
2
)
)
)
FROM [dbo].[PharmacyEntry] PE
You're providing 2 as a second parameter to sum instead of round. Try this:
select SUM(Round((CAST(PE.GstTotal as float) * PE.Quantity) / 2 , 2))
FROM [dbo].[PharmacyEntry] PE

How to Print decimal Values

select total_hours, total_hour_present,
(total_hour_present / total_hours) * 100 as total_hour_present
from attendancereport_subject
where roll_no = '08ME001'
try this
select total_hours,
total_hour_present,
(cast (total_hour_present as decimal) / total_hours) * 100 as total_hour_present
from attendancereport_subject
where roll_no = '08ME001'
This is a guess, but if the 2 hour columns are integers then you will most probably get zero, since the result will always be less than 1. So try something like this:
convert(decimal(10,2),(convert(decimal(10,2),total_hour_present) / total_hours) * 100) as total_hour_present
If you're missing decimals in the result, then that leads to the assumption that both "total_hour_present" and "total_hours" are integers.
Because in SQL Server, when an INT is divided by an INT then it returns an INT.
So you could cast or convert the total_hours to a float or a decimal.
So that the division returns a number with decimal values.
Example 1
(total_hour_present / cast(total_hours as decimal(10,0))) * 100 as percentage_hour_present
Example 2
(total_hour_present / convert(float, total_hours)) * 100 as percentage_hour_present
below should work
select total_hours, total_hour_present,
((total_hour_present*1.00) / (total_hours*1.00)) * 100.000 as total_hour_present
from attendancereport_subject
where roll_no = '08ME001'
you need to cast the value to decimal, Try this:
select total_hours, total_hour_present,
(cast(total_hour_present as decimal(18,0)) / total_hours) * 100 as
total_hour_present
from attendancereport_subject
where roll_no = '08ME001'

Teradata SUBSTRING Index Out of Bounds

This query works:
SELECT
TOP 100 SUBSTRING(column_name FROM 6 FOR CHARACTER_LENGTH(column_name) - 5) AS X
FROM db_name.table_name
But the following query (with WHERE clause added) does not execute.
SELECT
TOP 100 SUBSTRING(column_name FROM 6 FOR CHARACTER_LENGTH(column_name) - 5) AS X
FROM db_name.table_name
WHERE NOT EXISTS
(
SELECT 1
FROM db_name2.lookup_name H
WHERE H.SRC_NUM1 = X
AND H.SRC_TYPE = 11
)
The query above throws
SELECT Failed. 2663: SUBSTR: string subscript out of bounds in table_name.column_name
However, this following one works (original SELECT is nested)
SELECT *
FROM (
SELECT
TOP 100 SUBSTRING(column_name FROM 6 FOR CHARACTER_LENGTH(column_name) - 5) AS X
FROM db_name.table_name
) A
WHERE NOT EXISTS
(
SELECT 1
FROM db_name2.lookup_name H
WHERE H.SRC_NUM1 = X
AND H.SRC_TYPE = 11
)
Why is that so? I am using SQL assistant to execute the queries but I doubt it is of relevance.
Try to change (maybe error is caused when column_name's lenght is less then 6):
SELECT
TOP 100 CASE WHEN CHARACTER_LENGTH(column_name)>5
THEN SUBSTRING(column_name FROM 6 FOR CHARACTER_LENGTH(column_name) - 5)
ELSE NULL END AS X
FROM db_name.table_name

SQL - Return all rows for ID where one row meets condition A,B,or C

I'm trying to return all rows for a particular IDs where a condition is met in any one of the rows tied to those IDs. Pardon me being a newbie to SQL... Example below:
ID * Line * # *
12 * 1 * A *
12 * 2 * B *
12 * 3 * X *
12 * 4 * Y *
15 * 1 * A *
15 * 2 * B *
15 * 3 * C *
Not sure what the code would be other than my select and condition = (X, Y, or Z) to return:
ID * Line * # *
12 * 1 * A * <-- doesn't include X, Y, or Z but is part of the ID which
12 * 2 * B * <-- has X in another row of that ID
12 * 3 * X *
12 * 4 * Y *
I'm wanting to pull all row records despite not meeting the condition as long as they're part of the ID that has a row that meets the condition.
Thanks for the help!
* Edit: Including code attempted*
SELECT ID
,LINE
,#
WHERE ID,
IN (
SELECT ID
WHERE # IN ('X','Y','Z'))
Results:
ID LINE #
12 3 X
12 4 Y
What I need:
ID LINE #
12 1 A
12 2 B
12 3 X
12 4 Y
I almost feel like I need to create a temp table of ID & LINE using my condition of IN('X','Y','Z') and then inner join on ID for all LINE(s) not X,Y,Z. I think that may work, but I haven't learned how to use temp tables yet. I'm a little troubled because I'm using a query, which I've simplified a ton here, where I'm selecting 18 fields that join in 7 other tables. I think this is just complicating my understanding of the subquery, not so much the subquery being affected by that.
Thanks all for the help and answers so far!
You can use a subquery and IN for this.
Select *
From YourTable
where ID in (select ID from YourTable where # in ('X','Y','Z'))
Just a note, there is no 12 * 4 * C * in your data but I think it's just a type-o in your results and should be 12 * 4 * Y *
Besides the subquery approach you might also try an OLAP-function (Depending on the actual data this might be better or worse, of course)
In Teradata you can apply QUALIFY:
Select *
From YourTable
qualify -- check if any row with the same ID has X/Y/Z
max(case when ID in ('X','Y','Z') then 1 else 0 end)
over (partition by ID) = 1
In SQL Server you have to use a Derived Table/CTE:
Select *
From
( Select *,
max(case when ID in ('X','Y','Z') then 1 else 0 end)
over (partition by ID) as flag
from YourTable
) as dt
where flag = 1

T-SQL average rounded to the closest integer

I'm not sure if this has been asked before, but how do I get the average rounded to the closest integer in T-SQL?
This should do it. You might need a GROUP BY on the End depending on what you are looking for the average of.
SELECT CONVERT(int,ROUND(AVG(ColumnName),0))
FROM
TableName
EDIT: This question is more interesting than I first thought.
If we set up a dummy table like so...
WITH CTE
AS
(
SELECT 3 AS Rating
UNION SELECT 4
UNION SELECT 7
)
SELECT AVG(Rating)
FROM
CTE
We get an integer average of 4
However if we do this
WITH CTE
AS
(
SELECT 3.0 AS Rating
UNION SELECT 4.0
UNION SELECT 7.0
)
SELECT AVG(Rating)
FROM
CTE
We get a decimal average of 4.666..etc
So it looks like the way to go is
WITH CTE
AS
(
SELECT 3 AS Rating
UNION SELECT 4
UNION SELECT 7
)
SELECT CONVERT(int,ROUND(AVG(CONVERT(decimal,Rating)),0))
FROM CTE
Which will return an integer value of 5 which is what you are looking for.
If you are in SQL Server, just use round(avg(column * 1.0), 0).
The reason for * 1.0 is because sql server in some cases returns calculations using the same datatype of the values used in the calculation. So, if you calculate the average of 3, 4 and 4, the result is 3.66..., but the datatype of the result is integer, therefore the sql server will truncate 3.66... to 3, using * 1.0 implicit convert the input to a decimal.
Alternatively, you can convert or cast the values before the average calculation, like cast(column as decimal) instead of using the * 1.0 trick.
If your column it's not a integer column, you can remove the * 1.0.
PS: the result of round(avg(column * 1.0), 0) still is a decimal, you can explicit convert it using convert(int, round(avg(column * 1.0), 0), 0) or just let whatever language you are using do the job (it's a implicit conversion)
Select cast(AVG(columnname) as integer)
This worked for it:
CONVERT(int,ROUND(AVG(CAST(COLUMN-NAME AS DECIMAL)) ,0))
Isn't there a shorter way of doing it though?
T-SQL2018.
CAST(ROUND(COLUMN, 0) AS INT) This code does the job for me and gives the output I require so a 4.8 becomes 5.
whereas
CAST(AVG(COLUMN) AS INT) This code almost does the job but rounds down, so 4.8 becomes a 4 and not 5.
select cast(avg(a+.5) as int) from
(select 1 a union all select 2) b
If you don't like shortcuts, you could use the long way:
select round(avg(cast(a as real)), 0)
from (select 1 a union all select 2) b
The following statements are equivalent:
-- the original code
CONVERT(int, ROUND(AVG(CAST(mycolumn AS DECIMAL)) ,0))
-- using '1e0 * column' implicitly converts mycolumn value to float
CONVERT(int, ROUND(AVG(1e0 * mycolumn) ,0))
-- the conversion to INT already rounds the value
CONVERT(INT, AVG(1e0 * mycolumn))
On SQL 2014,
select round(94,-1)
select round(95,-1)