UPDATE SQL Command not saving the results - tsql

I looked though the forum but I couldn't find a issue like mine.
Essentially I have a table called [p005_MMAT].[dbo].[Storage_Max]. It has three columns Date, HistValue and Tag_ID. I want to make all the values in 'HistValue' column to have 2 decimal places. For example if a number is 1.1, I want it to be 1.10 or if its 1 then also I want it to look like 1.00.
Here is the sql update statement I am using
update [p005_MMAT].[dbo].[Storage_Max]
set [HistValue] = cast([HistValue] as decimal (10,2))
where [Tag_ID] = 94
After executing the query it says 3339 rows affected but when I perform a simple select statement it appears the column had no affect of. I have used that cast function in select statement and it adds two decimal places.
Please advice.

The problem is the datatype and SQL Server. Float or real will not have the trailing zeros. You either have to change the datatype of the column or just deal with it and handle the formatting in your queries or application.
You could run something like the following
select
cast([HistValue] as decimal (10,2))
from [p005_MMAT].[dbo].[Storage_Max]
where [Tag_ID] = 94

Related

In PostgreSQL, is it possible to have a default format for real columns?

In PostgreSQL, I have a column with people's height in meters. If the height is, say 1.75 m, it shows properly, but if the height is 1.70 m, it shows as 1.7. I would like to have this already formatted to two decimal places, showing as 1.70 without formatting in each and every SQL call. Can I specify this in the table creation? Or a stored procedure, or something? I've seen a few things about timestamps, but not for real fields. Knowing how to format the decimal point as a colon (1,70) would be a plus.
Basically, presentation and "cosmetics" are the job of the application, not the database.
Having a default number of decimal places for floats would also create a problem, because the data returned by the database would not be the actual data in the column. So if you did a SELECT and it returned a value of 1.75, then if you searched for this value, you might not find it because the actual value stored was not 1.75 but 1.7499999999 and it was only rounded for display.
Potential solutions:
If you want to store a specified number of digits, use NUMERIC. This will solve the 1.7499999999 problem above. If you use NUMERIC, when doing a SELECT you get the actual contents of the column.
In your app, if you use an ORM, use a Decimal (or similar) type for the column with the appropriate settings so it displays the way you want.
Or create a view with the format applied to the column, but in this case if you want the trailing zero, the type will be text and not float, and it will not be searchable unless you create an extra index on it.
Generated column with the number formatted as you want, maybe easier than a view

Postgres resampling time series data

I have OHLCV data of stocks stored in 1-minute increments inside Postgres.
I am trying to resample data to 5 minutes interval. I have used this answer to generate the following SQL query.
Here is the SQL query generated:
SELECT
avg('open') AS open,
avg('high') AS high,
avg('low') AS low,
avg('close') AS close,
avg('volume') AS volume,
avg('open_interest') AS open_interest,
to_timestamp(floor(EXTRACT(epoch FROM 'timestamp') / 300) * 300) AS interval_alias
WHERE 'symbol'='IRFC-N8' GROUP BY interval_alias
I am getting this error:
sqlalchemy.exc.ProgrammingError: (psycopg2.ProgrammingError) function avg(unknown) is not unique
LINE 1: SELECT avg('open') AS open, avg('high') AS high, avg('low') ...
^
HINT: Could not choose a best candidate function. You might need to add explicit type casts.
Could you tell me what went wrong?
Edit 1: Code formatted for better rendering.
Edit 2: According to the answer below, I need to use double quotes around the parameter to the avg function. I am using sqlalchemy to generate the expressions and it is creating single quoted strings. Here is the part of code which generates the avg query:
cols = list()
cols.append(func.avg(self.p.open).label(self.p.open))
cols.append(func.avg(self.p.high).label(self.p.high))
cols.append(func.avg(self.p.low).label(self.p.low))
cols.append(func.avg(self.p.close).label(self.p.close))
cols.append(func.avg(self.p.volume).label(self.p.volume))
cols.append(func.avg(self.p.openinterest).label(self.p.openinterest))
seconds = self._get_seconds()
cols.append(func.to_timestamp(func.floor(func.extract("epoch", "timestamp") / seconds) * seconds).label("interval_alias"))
SqlAlchemy should have known better to use double quotes but its generating single quotes.
db<>fiddle
Your error is the use of single quotes ' instead of double quotes " in your AVG() calls. The single quotes mark texts but you want to name the columns for the average value. So you need the double quotes or can leave them (both variants are shown in the db fiddle).
Edit (The real problem):
It seems that self.p.columnname gives just the column's name, not the column itself. On SQLAlchemy the reference to a specific column is table.c.columnname for referencing the specific column. Please use the p instead of c.
Attention:
If you average all your data you may lose important data as the real minimum and maximum. You may want to aggregate with other functions as MIN or MAX. Maybe the WITHIN GROUP functions could help you.
https://www.postgresql.org/docs/current/static/functions-aggregate.html
https://www.postgresql.org/docs/9.5/static/functions-aggregate.html#FUNCTIONS-ORDEREDSET-TABLE

EntityFramework Core sometimes rounds average to 2 decimal places

When I call Average method on a decimal column that has SQL type of DECIMAL (18,8) I sometimes get my value rounded to 2 decimal places and sometimes not.
When I print it to console I get something like 0.010000 and 0.021232 so it seems like this happens whenever the value is less than 0.01.
I tried setting data type explicitly using ModelBuilder but it doesn't change anything.
While I was writing this questions I check SQL profiler and found the problem. When EF sends a query it casts the column to DECIMAL(18,2)
So the question now becomes 'Why would they do this and how can I select desired precision?' Is this a bug or a feature?

Rounding to a variable number of decimal places in SSRS

I am trying to find a way to round a field in SSRS to a dynamic number of decimal places. I know I can format it dynamically, and it may eventually come to that, but many of my users are going to take this report directly to Excel and are going to want to have actual numeric fields.
My t-SQL code includes these declared variables:
NumLong01 DECIMAL(23,8)
, NumLongDP01 INTEGER
The first set of entries in this table is for headers and rounding parameters. So I add values for these two as:
NULL
,4
and then I add the actual table values as:
543210987654321.87654321
,NULL
That way I can put a whole series of numbers into the table but they all have to be formatted the same way.
Running this query yields:
When I go to ReportBuilder, my field has this expression:
=Fields!NumLong01.Value
If I want to format a certain number of decimal places, I can just do this:
=Round(Fields!NumLong01.Value,2) or some such. What I tried to do, though, was to make it dynamic:
=Round(Fields!NumLong01.Value,First(Fields!NumLongDP01.Value, "DataSet1"))
This ended up rounding to 0 decimal places. I subsequently learned--by just using the second half in my field--that this was a NULL value. So I tried Sum instead of First--again, just in my field--and got the 4 that I expected. Great, so now I had my number, and I just put that in as my rounding:
=Round(Fields!NumLong01.Value,Sum(Fields!NumLongDP01.Value, "DataSet1"))
Only problem is, this yields an error. Next I asked myself if maybe it wasn't seeing this as a number for some reason. So i just added it onto my field. No problems. So I really don't know what it's doing. Is it thinking that this field might become so long that it will round to an illegal number of decimals places?
Now, I can do this:
=IIf(Sum(Fields!NumLongDP01.Value, "DataSet1") = 8,Round(Fields!NumLong01.Value,8),IIf(Sum(Fields!NumLongDP01.Value, "DataSet1") = 7,Round(Fields!NumLong01.Value,7),IIf(Sum(Fields!NumLongDP01.Value, "DataSet1") = 6,Round(Fields!NumLong01.Value,6),IIf(Sum(Fields!NumLongDP01.Value, "DataSet1") = 5,Round(Fields!NumLong01.Value,5),IIf(Sum(Fields!NumLongDP01.Value, "DataSet1") = 4,Round(Fields!NumLong01.Value,4),IIf(Sum(Fields!NumLongDP01.Value, "DataSet1") = 3,Round(Fields!NumLong01.Value,3),IIf(Sum(Fields!NumLongDP01.Value, "DataSet1") = 2,Round(Fields!NumLong01.Value,2),IIf(Sum(Fields!NumLongDP01.Value, "DataSet1") = 1,Round(Fields!NumLong01.Value,1),Round(Fields!NumLong01.Value,0)))))))))
...and that works. But it seems like such a ridiculous way to go about it.
I'm also comfortable passing only rounded numbers out of t-SQL. But then I run into the problem of showing only a certain number of decimals on the report, because in the number formatting it doesn't allow for a dynamic number of decimal places for some reason.
Any ideas would be appreciated.
This isn't an exhaustive list of ways to accomplish dynamic rounding or number formatting as you can achieve this using custom code in the report or by adapting your dataset's SQL query.
Using Rounding:
The first set of entries in this table is for headers and rounding parameters. That way I can put a whole series of numbers into the table but they all have to be formatted the same way.
To avoid building expressions in your report that require aggregate functions such First and Sum and generating a blank row that you then have to remove, consider just entering the number of decimal places for every row instead of using a header row. The costs (storage and expression evaluation) are low even if it seems redundant.
This means that instead of using: =Round(Fields!NumLong01.Value,First(Fields!NumLongDP01.Value, "DataSet1")) you can use =Round(Fields!NumLong01.Value,Fields!NumLongDP01.Value) either as an expression or as a calculated field in DataSet1 or whatever your dataset is called.
Using Number Formatting:
But then I run into the problem of showing only a certain number of decimals on the report, because in the number formatting it doesn't allow for a dynamic number of decimal places for some reason.
You can define custom formatting for the NumLong01 field in the report and make it dynamic using an expression to build your custom formatting string.
Open the Text Box Properties for the NumLong01 textbox or tablix field
Open Number tab and select Custom from the Category list
Click the fx button and use the following expression ="0." + StrDup(First(Fields!NumLongDP01.Value, "DataSet1"), "0")
Using your example data, this expression would produce the custom formatting string 0.0000 which changes 543210987654321.87654321 to 543210987654321.8765. For your information, StrDup duplicates the specified string X number of times.
In cases where the fractional part of the number is less than the decimal precision required, this formatting string will pad it with 0s. If that's not desired, change the string to be duplicated to "#" like so: StrDup(First(Fields!NumLongDP01.Value, "DataSet1"), "#").
You can also use this method as a calculated field in the dataset but only if you have removed the header row and are entering the decimal places for every row as mentioned earlier. This is because you can't use the aggregate function in the calculated field expression.
To do this, add a calculated field to your dataset with the expression: =Format(Fields!NumLong01.Value, "0." + StrDup(Fields!NumLongDP01.Value, "0"))

Converting / Casting an nVarChar with Comma Separator to Decimal

I am supporting an ETL process that transforms flat-file inputs into a SqlServer database table. The code is almost 100% T-SQL and runs inside the DB. I do not own the code and cannot change the workflow. I can only help configure the "translation" SQL that takes the file data and converts it to table data (more on this later).
Now that the disclaimers are out of the way...
One of our file providers recently changed how they represent a monetary amount from '12345.67' to '12,345.67'. Our SQL that transforms the value looks like SELECT FLOOR( CAST([inputValue] AS DECIMAL(24,10))) and no longer works. I.e., the comma breaks the cast.
Given that I have to store the final value as Decimal (24,10) datatype (yes, I realize the FLOOR wipes out all post-decimal-point precision - the designer was not in sync with the customer), what can I do to cast this string efficiently?'
Thank you for your ideas.
try using REPLACE (Transact-SQL):
SELECT REPLACE('12,345.67',',','')
OUTPUT:
12345.67
so it would be:
SELECT FLOOR( CAST(REPLACE([input value],',','') AS DECIMAL(24,10)))
This works for me:
DECLARE #foo NVARCHAR(100)
SET #foo='12,345.67'
SELECT FLOOR(CAST(REPLACE(#foo,',','') AS DECIMAL(24,10)))
This is probably only valid for collations/culture where the comma is not the decimal separator (ie: Spanish)
While not necessarily the best approach for my situation, I wanted to leave a potential solution for future use that we uncovered while researching this problem.
It appears that the SqlServer datatype MONEY can be used as a direct cast for strings with a comma separating the non-decimal portion. So, where SELECT CAST('12,345.56' AS DECIMAL(24,10)) fails, SELECT CAST('12,345.56' AS MONEY) will succeed.
One caveat is that the MONEY datatype has a precision of 4 decimal places and would require an explicit cast to get it to DECIMAL, should you need it.
SELECT FLOOR (CAST(REPLACE([inputValue], ',', '') AS DECIMAL(24,10)))