TSQL - how to avoid truncating or rounding - sql-server

For some reason, the data in the ORIGINAL_BOOK column (even though it has 2 decimals (eg. 876.76)), the statement below truncates the decimals. I want the decimals to be visible as well. Can someone suggest how to fix this issue please?
Case
When [DN].[SETTLEMENT_DATE] > [DN].[AS_OF_DATE]
Then Cast([DN].[ORIGINAL_BOOK] as decimal(28, 2))
Else Cast([DN].[CURRENT_BOOK] as decimal(28, 2))
End

I can't be sure because you only say that the type of the fields involved is NUMERIC, without specifying any precision or scale, however if your source fields really are just NUMERIC type, SQL defaults to NUMERIC(18,0) (as per MSDN documentation here) and so you will only be able to store values with a scale of zero (i.e. no value after the decimal place) and any values written to these fields with a greater scale (i.e. data after the decimal place) will be rounded accordingly:
CREATE TABLE dn (
ORIGINAL_BOOK NUMERIC,
CURRENT_BOOK NUMERIC
)
INSERT INTO dn
SELECT 876.76, 423.75
UNION
SELECT 0, 0
UNION
SELECT 1.1, 6.5
UNION
SELECT 12, 54
UNION
SELECT 5.789, 6.321
SELECT CAST(dn.ORIGINAL_BOOK AS DECIMAL(28,2)) AS ORIGINAL_BOOK_CONV,
CAST(dn.CURRENT_BOOK AS DECIMAL(28,2)) AS CURRENT_BOOK_CONV
FROM dn
DROP TABLE dn
gives results:
/----------------------------------------\
| ORIGINAL_BOOK_CONV | CURRENT_BOOK_CONV |
|--------------------+-------------------|
| 0.00 | 0.00 |
| 1.00 | 7.00 |
| 6.00 | 6.00 |
| 12.00 | 54.00 |
| 877.00 | 424.00 |
\----------------------------------------/
Increasing the scale of the field in the table will allow values with greater numbers of decimal places to be stored and your CAST call will then reduce the number of decimal places if appropriate:
CREATE TABLE dn (
ORIGINAL_BOOK NUMERIC(28,3),
CURRENT_BOOK NUMERIC(28,3)
)
INSERT INTO dn
SELECT 876.76, 423.75
UNION
SELECT 0, 0
UNION
SELECT 1.1, 6.5
UNION
SELECT 12, 54
UNION
SELECT 5.789, 6.321
SELECT CAST(dn.ORIGINAL_BOOK AS DECIMAL(28,2)) AS ORIGINAL_BOOK_CONV,
CAST(dn.CURRENT_BOOK AS DECIMAL(28,2)) AS CURRENT_BOOK_CONV
FROM dn
DROP TABLE dn
gives results:
/----------------------------------------\
| ORIGINAL_BOOK_CONV | CURRENT_BOOK_CONV |
|--------------------+-------------------|
| 0.00 | 0.00 |
| 1.10 | 6.50 |
| 5.79 | 6.32 |
| 12.00 | 54.00 |
| 876.76 | 423.75 |
\----------------------------------------/
If you are sure that your table fields are capable of containing numeric values to more than zero decimal places (i.e. scale > 0), please post the CREATE TABLE script for the table (you can get this from SSMS) or a screenshot of the Column listing so we can see the true type of the underlying fields. It would also be useful to see values SELECTed from the fields without any CASTing so we can see how the data is presented without any conversion.

Related

ROUND T-SQL of values 0.0

I am working on a Select statement where I have to show the average of genres but the result must show the 0 values as 0.0
My code is
SELECT genreName AS 'Genre'
, CAST(CASE WHEN
AVG(rateValue) IS NULL
THEN 0
ELSE ROUND(FORMAT(AVG(rateValue),2),0.0)
END AS FLOAT) AS 'Average Rating'
FROM [IMDB].[FilmGenre] flmgnr
LEFT JOIN [IMDB].[FilmGenreAllocation] flmgnrall
ON(flmgnrall.genreId = flmgnr.genreId)
LEFT JOIN [IMDB].[Film] flm
ON(flm.filmId = flmgnrall.filmId)
LEFT JOIN [IMDB].[FilmReview] flmrvw
ON(flmrvw.filmId = flmgnrall.filmId)
GROUP BY genreName;
GO
and the result must be
+---------+----------------+
| Genre | Average Rating |
+---------+----------------+
| Action | 0.00 |
| Comedy | 4.67 |
| Crime | 4.50 |
| Drama | 4.50 |
| Family | 0.00 |
| Mystery | 4.40 |
+---------+----------------+
Agree with Zohar Peled. This should really be in the presentation layer.
However, if you must, one option is to use the Format() function if 2012+, or just cast as decimal
...Format(IsNull(AVG(rateValue),0),'#0.00')
or
...Cast(IsNull(AVG(rateValue),0) as decimal(18,2))
Don't cast your values to float, but rather to DECIMAL(18,2) like so:
CAST(CASE WHEN
AVG(rateValue) IS NULL
THEN 0
ELSE ROUND(FORMAT(AVG(rateValue),2),0.0)
END AS DECIMAL(18,2)) AS 'Average Rating'
Instead of
THEN 0
try
THEN 0.0
With 0 you told to SQL Server - case is int data type.
Or better way from:
CAST(CASE WHEN
AVG(rateValue) IS NULL
THEN 0
ELSE ROUND(FORMAT(AVG(rateValue),2),0.0)
END AS FLOAT)
Make:
1.0 * ROUND(ISNULL(AVG(rateValue), 0), 1) -- isnull assume data type from first item

Query to find closest number and return the associated discount in SQL server

Here is the exact situation:
Monthly_Budget | Extra_Bonus
---------------------------
300,000,000 | 0.40
420,000,000 | 0.60
580,000,000 | 0.90
1,000,000,000 | 1.20
1,600,000,000 | 1.45
2,900,000,000 | 1.55
4,160,000,000 | 1.65
6,600,000,000 | 1.80
10,000,000,000 | 2.10
14,160,000,000 | 2.25
20,000,000,000 | 2.60
26,000,000,000 | 3.00
33,000,000,000 | 3.40
50,000,000,000 | 4.00
73,000,000,000 | 4.50
The first Column shows the budget and second one indicates the relevant bonus.
The rule says for example if the budget is 300,000,000 or 350,000,000 or 410,000,000 (anything less than next record) the bonus will be 0.40 (the previous Record).
my question is how i can query this exact given example?
SELECT Extra Bonus
FROM Monthly_Budget_TBL
WHERE Monthly_Budget .....?
Thanks
declare #actualBudget integer = 350000000
-- --------------------------------
Select Extra_Bonus from table
Where Monthly_Budget =
(Select Max(Monthly_Budget) from table
Where Monthly_Budget <= #actualBudget)
So you have some budget as input and want to get the closest bonus that has a budget lower or equal your input?
in that case this should work:
SELECT Extra_Bonus, {exampleBudgetVariable} - Monthly_Budget as diff FROM Monthly_Budget_TBL
WHERE {exampleBudgetVariable} - Monthly_Budget >= 0
ORDER BY diff ASC
LIMIT 1
{exampleBudgetVariable} should be replaced with your example budget in whatever language you are coding
In order to demonstrate this properly, I'm going to make up a couple additional tables. Let's pretend you have a Employees table with an EmployeeID column and a Name column, and MonthlySalesSummary table with an EmployeeID column and SalesAmount column. It's this SalesAmount value that you want to match up against the budget for each salesperson. Here's what the query might look like:
SELECT e.Name, b.Extra_Bonus
FROM Employee e
INNER JOIN MonthlySalesSummary s ON s.EmployeeID = e.EmployeeID
CROSS APPLY (
SELECT TOP 1 Extra_Bonus FROM Monthly_Budget_TBL WHERE Monthly_Budget > SalesAmount ORDER BY Monthly_Budget
) b

SQL nested case with value differences between 2 tables

I have imported two Excel sheets as tables in Microsoft's SQL Server Management Server 2007, and they are both identical, except for the fact that they are from 2 different dates.
I'm looking to do 2 things that I'm struggling to do:
Calculate the monthly difference between values for the 2 tables which I can do with cast, and
inner join, but I'm not successful in using those values to be able to sum those values with a nested case like so:
SELECT SUM(
CASE WHEN ID <>'MISSING' THEN
CASE WHEN SUM(VALUE)>=0 THEN
SUM(VALUE)
ELSE
0
END
END)
I've tried many different ways, but one of the main errors I get is:
Cannot perform an aggregate function on an expression containing an aggregate or a subquery.
The data would like so:
dbo.Table1
date(dd/mm/yy) | name | id | value
---------------+------+---------+-------
1/1/14 | A | MISSING | 56
1/1/14 | A | MISSING | -1
1/1/14 | B | YES | 56
1/1/14 | B | YES | -1
dbo.Table2
date(dd/mm/yy) | name | id | value
---------------+------+---------+-------
1/2/14 | A | MISSING | 24
1/2/14 | A | MISSING | -11
1/2/14 | B | YES | 24
1/2/14 | B | YES | -11
Don't use SUM inside another SUM, just keep only the outer SUM. Also your outer CASE has no ELSE part, but you'd probably want to have 0 in that scenario as well, so why not use a single CASE condition?
SELECT SUM(CASE WHEN ID <> 'MISSING' AND VALUE >= 0 THEN VALUE ELSE 0 END)

Dynamically generating HighCharts with SQL Server database

I have a SQL Server database with columns ID, DateTime and Double.
For example:
001 | 2014:06:30 10:25AM | 101.5
001 | 2014:06:30 10:26AM | 102.5
001 | 2014:06:30 10:27AM | 111.5
002 | 2014:06:30 10:27AM | 101.4
002 | 2014:06:30 10:28AM | 123.1
003 | 2014:06:30 10:29AM | 114.9
003 | 2014:06:30 10:30AM | 171.6
003 | 2014:06:30 10:31AM | 131.2
003 | 2014:06:30 10:31AM | 119.4
I want my highcharts to be a scatter chart with Datetime values as the X-Axis and the Double Values as the Y-Axis. These values are on individual series which are defined by the ID column (i.e Series 001 will have 3 points).
The problem I'm having is that the ID Column can change. Based on the users selection my query is adjusting. In one instance I can have 5 series (5 different ID's) and in the example above only 3 series.
I was wondering how to create the series in a loop which will loop based on the unique ID's and to pass in the x and y values as arrays.
I've been trying to figure this out for a few days now and looked everywhere for a solution so anything helps at this moment. Currently I'm using Visual Studio 2013 and VB.Net with only a front and back end page. My data is in a MySQL Server where I'm already querying the values correctly based on user selection.
You could have SQL Server construct and return the array as a string
Declare #Table table (id varchar(10),SomeDate varchar(25),SomeValue money)
Insert into #Table values
('001','2014:06:30 10:25AM',101.5),
('001','2014:06:30 10:26AM',102.5),
('001','2014:06:30 10:27AM',111.5),
('002','2014:06:30 10:27AM',101.4),
('002','2014:06:30 10:28AM',123.1),
('003','2014:06:30 10:29AM',114.9),
('003','2014:06:30 10:30AM',171.6),
('003','2014:06:30 10:31AM',131.2),
('003','2014:06:30 10:31AM',119.4)
SELECT id, array = '['+STUFF((
SELECT ',["' + SomeDate+'",'+cast(SomeValue as varchar(25))+']' FROM #Table
WHERE id = x.id
FOR XML PATH(''), TYPE).value('.[1]', 'nvarchar(max)'), 1, 1, '')
+']'
FROM #Table AS x
GROUP BY ID
Returns
id array
001 [["2014:06:30 10:25AM",101.50],["2014:06:30 10:26AM",102.50],["2014:06:30 10:27AM",111.50]]
002 [["2014:06:30 10:27AM",101.40],["2014:06:30 10:28AM",123.10]]
003 [["2014:06:30 10:29AM",114.90],["2014:06:30 10:30AM",171.60],["2014:06:30 10:31AM",131.20],["2014:06:30 10:31AM",119.40]]

PIVOT in SQL Server with no aggregate function

I have a table like this (lets call it T1):
Date | Category | Data
-------------------------------
2014-01-01 | 1 | 1.0
2014-01-01 | 2 | 2.0
2014-01-01 | 3 | 3.0
2014-01-02 | 1 | 4.0
2014-01-02 | 2 | 5.0
2014-01-02 | 3 | 6.0
Note that Data are floating point numbers, not sequential, I just made them that way so it's easier to see where they land up in the result. There is also a table translating the Category numbers to names (lets say (T2):
Category | Name
----------------
1 | A
2 | B
3 | C
And I would like a query that could return:
Date | B | C
---------------------------
2014-01-01 | 2.0 | 3.0
2014-01-02 | 5.0 | 6.0
I thought about trying with PIVOT which I haven't used before, however, I can't find a way to do it without using a aggregation function. But then I though that since I only actually have a single row per Date / Category combo using an aggregation like AVG should work. This is my try based on these docs:
SELECT [Date],
[2] as B,
[3] as C
FROM (SELECT * FROM T1 WHERE Category >= 2) AS SourceTable
PIVOT
(
AVG(Data)
FOR Category IN ([2], [3])
) AS PivotTable;
Which gets kind of close:
Date | B | C
---------------------------
2014-01-01 | 2.0 | NULL
2014-01-01 | NULL | 3.0
2014-01-02 | 5.0 | NULL
2014-01-02 | NULL | 6.0
But how do I get rid of the nulls and get all the same dates to be on the same row?
If you only have one value for each result, you can use any of the aggregate functions - eg: MAX
select * from
(select t.date, t.data,c.name from t1 t inner join category c on t.category = c.category) s
pivot (max(data) for name in ([b],[c])) p
The first part is the source data
select t.date, t.data,c.name from t1 t inner join category c on t.category = c.category
Then the pivot rearranges it, creating columns for the in values from the name column, and using the aggregate (max(data)) for the value in that column, which should give the desired results.
If you're getting nulls, it's probably from using select * in your source query, rather than selecting the required fields - ie: replace
SELECT * FROM T1 WHERE Category >= 2
with
select category, data FROM T1 WHERE Category >= 2

Resources