I ran into something that seems odd. SQL Server appears to be rounding some DateTime values inappropriately when I save them to datetime columns. I suspect I'm missing something, but I can't spot it. I'm running this test against SQL Server 2008 using .NET 4.0. The following should illustrate the issue:
I have created a table in in SQL Server called Timestamps. It has two columns:
id - bigint, Identity, PK
timestamp - datetime
I also created a simple test that does the following:
Gets the current time, truncating the value to millisecond precision
Saved the truncated time to Timestamps
Retrieved the datetime` value from the DB and compared it to the original (truncated) DateTime object.
public static void RoundTest()
{
DateTime preTruncation = DateTime.UtcNow;
DateTime truncated = preTruncation.TruncateToMilliseconds();
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["test"].ConnectionString))
{
conn.Open();
SqlCommand cmd = new SqlCommand(#"INSERT INTO Timestamps(timestamp)
VALUES(#savedTime);
SELECT SCOPE_IDENTITY() AS id");
cmd.Parameters.Add(new SqlParameter("savedTime", truncated));
cmd.Connection = conn;
var id = cmd.ExecuteScalar();
SqlCommand get = new SqlCommand(#"SELECT timestamp FROM Timestamps
WHERE id = #id");
get.Parameters.Add(new SqlParameter("id", id));
get.Connection = conn;
DateTime retrieved = (DateTime)get.ExecuteScalar();
if (retrieved != truncated)
{
Console.WriteLine("original: " + preTruncation.TimeOfDay);
Console.WriteLine("truncated: " + truncated.TimeOfDay);
Console.WriteLine("retrieved: " + retrieved.TimeOfDay);
Console.WriteLine();
}
}
}
Although I expect the truncated value to be equivalent to the value returned back from the DB, that is not always the case. Here's some sample output:
original: 19:59:13.4049965
truncated: 19:59:13.4040000
retrieved: 19:59:13.4030000
original: 19:59:14.4989965
truncated: 19:59:14.4980000
retrieved: 19:59:14.4970000
original: 19:59:15.4749965
truncated: 19:59:15.4740000
retrieved: 19:59:15.4730000
original: 19:59:30.1549965
truncated: 19:59:30.1540000
retrieved: 19:59:30.1530000
TruncateToMilliseconds() looks like this:
public static DateTime TruncateToMilliseconds(this DateTime t)
{
return new DateTime(t.Year, t.Month, t.Day, t.Hour, t.Minute, t.Second, t.Millisecond);
}
What gives? Is this really inappropriate rounding, or am I making a mistaken assumption here?
Datetime is only accurate to 3ms. Therefore it'll round to the nearest multiple of 3ms. To overcome this, look at the datetime2. Note that this is for SQL2008+ only
EDIT: it's not quite only to 3ms. It's rounded to increments of of .000, .003, or .007 seconds
Related
I ran into something that seems odd. SQL Server appears to be rounding some DateTime values inappropriately when I save them to datetime columns. I suspect I'm missing something, but I can't spot it. I'm running this test against SQL Server 2008 using .NET 4.0. The following should illustrate the issue:
I have created a table in in SQL Server called Timestamps. It has two columns:
id - bigint, Identity, PK
timestamp - datetime
I also created a simple test that does the following:
Gets the current time, truncating the value to millisecond precision
Saved the truncated time to Timestamps
Retrieved the datetime` value from the DB and compared it to the original (truncated) DateTime object.
public static void RoundTest()
{
DateTime preTruncation = DateTime.UtcNow;
DateTime truncated = preTruncation.TruncateToMilliseconds();
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["test"].ConnectionString))
{
conn.Open();
SqlCommand cmd = new SqlCommand(#"INSERT INTO Timestamps(timestamp)
VALUES(#savedTime);
SELECT SCOPE_IDENTITY() AS id");
cmd.Parameters.Add(new SqlParameter("savedTime", truncated));
cmd.Connection = conn;
var id = cmd.ExecuteScalar();
SqlCommand get = new SqlCommand(#"SELECT timestamp FROM Timestamps
WHERE id = #id");
get.Parameters.Add(new SqlParameter("id", id));
get.Connection = conn;
DateTime retrieved = (DateTime)get.ExecuteScalar();
if (retrieved != truncated)
{
Console.WriteLine("original: " + preTruncation.TimeOfDay);
Console.WriteLine("truncated: " + truncated.TimeOfDay);
Console.WriteLine("retrieved: " + retrieved.TimeOfDay);
Console.WriteLine();
}
}
}
Although I expect the truncated value to be equivalent to the value returned back from the DB, that is not always the case. Here's some sample output:
original: 19:59:13.4049965
truncated: 19:59:13.4040000
retrieved: 19:59:13.4030000
original: 19:59:14.4989965
truncated: 19:59:14.4980000
retrieved: 19:59:14.4970000
original: 19:59:15.4749965
truncated: 19:59:15.4740000
retrieved: 19:59:15.4730000
original: 19:59:30.1549965
truncated: 19:59:30.1540000
retrieved: 19:59:30.1530000
TruncateToMilliseconds() looks like this:
public static DateTime TruncateToMilliseconds(this DateTime t)
{
return new DateTime(t.Year, t.Month, t.Day, t.Hour, t.Minute, t.Second, t.Millisecond);
}
What gives? Is this really inappropriate rounding, or am I making a mistaken assumption here?
Datetime is only accurate to 3ms. Therefore it'll round to the nearest multiple of 3ms. To overcome this, look at the datetime2. Note that this is for SQL2008+ only
EDIT: it's not quite only to 3ms. It's rounded to increments of of .000, .003, or .007 seconds
I am trying to write a code for correction of entries to my SQL Server database. I am a mechanical engineering student who has a programming class and I have never programmed before so I am not sure should I convert string to decimal and how. Last 2 rows contain 2 options I came up with. Second one is what I use for pure string, first one is a modification of formatting datetime.
This is my stored procedure:
ALTER PROCEDURE [dbo].[SP_RN_O_Ispravak]
#Br_RN_O bigint,
#Datum_O DateTime OUTPUT,
#Sifra_p int OUTPUT,
#Ime_P nvarchar (30) output,
#Prezime_P NVarChar(30) OUTPUT,
#Naziv_P nvarchar (50) output,
#Adresa_P nvarchar (50) OUTPUT,
#Telefon_P NVarChar(15) OUTPUT,
#Sifra_z int OUTPUT,
#Ime_Z nvarchar (30) output,
#Prezime_Z nvarchar (30) output,
#Sifra_kul nvarchar (3) OUTPUT,
#Naziv_Kul NVarChar(20) OUTPUT,
#Masa_O decimal (5,0) OUTPUT,
#Vlaga_O decimal (4,1) OUTPUT,
#Hek_Masa_O decimal (3,1) OUTPUT,
#Protein_O decimal (3,1) output,
#Cijena_O decimal (3,2) output
AS
SELECT #Br_RN_O=T_Otkup.Br_RN_O,
#Datum_O=T_Otkup.Datum_O,
#Sifra_p=T_Otkup.Sifra_p,
#Sifra_z=T_Otkup.Sifra_z,
#Sifra_kul=T_Otkup.Sifra_kul,
#Masa_O=T_Otkup.Masa_O,
#Vlaga_O=T_Otkup.Vlaga_O,
#Hek_Masa_O=T_Otkup.Hek_Masa_O,
#Protein_O=T_Otkup.Protein_O
FROM T_Otkup
WHERE (T_Otkup.Br_RN_O = #Br_RN_O)
SELECT #Prezime_P=Prezime_P
FROM T_Poljoprivrednik
WHERE Sifra_P=#Sifra_p
SELECT #Prezime_z=Prezime_Z
FROM T_Zaposlenik
WHERE Sifra_Z=#Sifra_z
SELECT #Naziv_kul=Naziv_Kul
FROM T_Kultura
WHERE Sifra_Kul=#Sifra_kul
RETURN
This procedure is supposed to pull the data from the database and place it in textboxes shown in the image.visual of whati'm trying to make
I managed to use the following code to convert decimal to string:
Dim cijenao As SqlParameter = New SqlParameter("#Cijena_O", Data.SqlDbType.Decimal, 3, 2)
cijenao.Direction = Data.ParameterDirection.Output
cijenao.Value = Cijena_O.Text
cmd.Parameters.Add(cijenao)
Masa_O.Text = Format(masao.Value, "#####").ToString
Vlaga_O.Text = Format(vlagao.Value, "###.#").ToString
Hek_Masa_O.Text = Format(hmasao.Value, "##.#").ToString
Protein_O.Text = Format(proto.Value, "##.#").ToString
However, it doesn't work for 2 decimal places like this:
Cijena_O.Text = Format(cijenao.Value, "#.##").ToString
I tried using the code posted by Mary, but it get the following message:
System.Data.SqlClient.SqlException: 'Procedure or function SP_RN_O_Ispravak has too many arguments specified.'
I've cleaned up this Sub, to properly scope the connection, and make sure the Connection and Command objects get disposed (via Using). It is always best to explicitly handle the data type conversions, such as using .ToString() on the .Value property of the parameters. Note I also parse the Long before assigning it to the input parameter (although you should Google the .TryParse() method and use that).
Protected Sub ISPRAVAK_NALOGA()
Using conn As New SqlConnection(<your connection string here>)
Using cmd As New SqlCommand("SP_RN_O_Ispravak", conn) With {.CommandType = CommandType.StoredProcedure}
With cmd
.Parameters.Add("#Br_RN_O", SqlDbType.BigInt).Value = Long.Parse(Br_RN_O.Text)
.Parameters.Add("#Masa_O", Data.SqlDbType.Decimal, 5, 0)
.Parameters("#Masa_O").Direction = ParameterDirection.Output
.Parameters.Add("#Vlaga_O", Data.SqlDbType.Decimal, 4, 1)
.Parameters("#Vlaga_O").Direction = ParameterDirection.Output
conn.Open()
.ExecuteNonQuery()
Masa_O.Text = .Parameters("#Masa_O").Value.ToString
Vlaga_O.Text = .Parameters("#Vlaga_O").Value.ToString
End With
End Using
End Using
End Sub
As to your code...
Check the available overloads for the Constructors for the Parameter class. https://learn.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlparameter?view=netframework-4.8#constructors There are none that match your code. Dim masao As SqlParameter = New SqlParameter("#Masa_O", Data.SqlDbType.Decimal, 5, 0) The final parameter of the constructor with 4 parameters is a string holding the name of the source column.
A Bigint in Sql Server maps to and Int64 in .net. (A Long in vb.net) This is a good reference for mapping datatypes from Sql Server to .net. https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/sql-server-data-type-mappings.
Now my code...
Declare the variable outside the using blocks.
The TryParse is a very clever method that not only test a string but fills the variable with the converted string when it succeeds. Return is functionly equivalent, in this case, to the vb.net specific Exit Sub . Return is often used in other languages (think C#).
Keep your databse objects local so you can control their closing and disposing. A Using block will do this for you even if there is an error. You don't need to create new variable for the parameters. They can be referred to by name in the Parameters collection. Set the values of maso an vlaga inside the Using block before the command is disposed.
After the database objects are duly discharges, we can set the values in the User Interface. Reguarding the .ToString method; N0 (the 0 is a zero) will give you a string containing the number with no decimal protion. The N stands for Number and the 0 is the number of decimal places. It adds commas to make the number easier to read and it will round as appropriate. See https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings for more details.
Ideally the database code and the UI code would be separated but that is for another day.
Protected Sub ISPRAVAK_NALOGA()
Dim maso As Decimal
Dim vlaga As Decimal
Dim InputNumber As Long
If Not Long.TryParse(Br_RN_O.Text, InputNumber) Then
MessageBox.Show("Please enter a valid number.")
Return
End If
Using conn As New SqlConnection("Your connection string")
Using cmd As New SqlCommand("SP_RN_O_Ispravak", conn)
cmd.CommandType = CommandType.StoredProcedure
With cmd.Parameters
.Add("#Br_RN_O", SqlDbType.BigInt).Value = InputNumber
.Add("#Masa_O", Data.SqlDbType.Decimal)
.Add("#Vlaga_O", Data.SqlDbType.Decimal)
End With
cmd.Parameters("#Vlaga_O").Direction = ParameterDirection.Output
cmd.Parameters("#Masa_O").Direction = Data.ParameterDirection.Output
conn.Open()
cmd.ExecuteNonQuery()
maso = CDec(cmd.Parameters("#Masa_O").Value)
vlaga = CDec(cmd.Parameters("#Vlaga_O").Value)
End Using
End Using
Masa_O.Text = maso.ToString("N0")
Vlaga_O.Text = vlaga.ToString
End Sub
I am a bit unsure of what you intent.
But if you want to know if you need to convert a number into a string before assigning the value to a textbox, then the answer is yes. You do need to convert it.
But there are a few things that you can do to display decimal values in a more readable way. For example, you can set the textbox customFormat to #########0.00 or ######,##0.00
I have the following connection string:
provider=SQLNCLI11;Server=[server];Database=[db];uid=[uid];pwd=[pwd]
and I have the following code:
OleDbCommand oComm = new OleDbCommand();
oComm.Connection = OleConnection;
oComm.Transaction = m_oleTran;
oComm.CommandText = sSQL;
oComm.CommandTimeout = TimeOut;
BuildParams(ref oComm, sCols, (object [])oVals);
if (oComm.Connection.State == ConnectionState.Closed)
oComm.Connection.Open();
m_RowsAffected = oComm.ExecuteNonQuery();
if (m_oleTran == null)
oComm.Connection.Close();
oComm.Dispose();
private void BuildParams(ref OleDbCommand oComm, string [] sCols, object [] oVals)
{
for (int i = 0; i< sCols.Length; i++)
{
if (sCols.Length > 0)
oComm.Parameters.AddWithValue(sCols[i], oVals[i]);
}
}
when I executed a simple update SQL statement, I got the following error
The fractional part of the provided time value overflows the scale of the corresponding SQL Server parameter or column. Increase bScale in DBPARAMBINDINFO or column scale to correct this error. at System.Data.OleDb.OleDbCommand.ExecuteReaderInternal(CommandBehavior behavior, String method) at System.Data.OleDb.OleDbCommand.ExecuteNonQuery()
Any ideas?
Thanks,
Solution used in DTSX Script Task with VB language.
For compliance reasons our customer asked us to change the SQL Server provider in a database version upgrade. And we started getting the same error when trying to save the dates. The date sent to the database was not on the correct scale. We could have changed the data type in the database, but we chose not to do so after we tested this very simple solution that worked. We just use a
.ToString("yyyy-MM-dd HH:mm:ss")
and send a string instead of a date with the correct size and it went through the provider and the database and saved without problems.
Provider=SQLNCLI11;
SOLUTION:
com.Parameters.AddWithValue("#COD_INTERFACE", SqlDbType.Int)
com.Parameters.AddWithValue("#COD_SEQUENCIAL", SqlDbType.Int)
com.Parameters.AddWithValue("#DTA_GERACAO", SqlDbType.DateTime)
com.Parameters.AddWithValue("#DTA_IMPORTACAO", SqlDbType.DateTime)
com.Parameters("#COD_INTERFACE").Value = CInt(Dts.Variables("User::intCodigoInterface").Value)
com.Parameters("#COD_SEQUENCIAL").Value = CInt(Dts.Variables("User::intSequencialArquivo").Value)
com.Parameters("#DTA_GERACAO").Value = CDate(Dts.Variables("User::dtaGeracaoArquivo").Value).ToString("yyyy-MM-dd HH:mm:ss")
com.Parameters("#DTA_IMPORTACAO").Value = Now.ToString("yyyy-MM-dd HH:mm:ss")
I am returning a static string from a stored procedure (in SQL Server 2008) as below:
select 'abcdefgh.........xyz'
If the static string length is exceeding more than some limit (eg:8kb) then only partial string (eg:7kb) is returned to the .net apps.
Though I tried in different ways like assigning static string to varchar(max) and selecting the variable, is still returning only partial string.
I should return complete string which could be of max of 5mb. So, main concerns:
What is the max string length I can return from a stored procedure
How to return 5 mb string from stored procedure to .net apps.
I request someone can help me to resolve this issue.
please find the code below
using (SqlCommand command = new SqlCommand(Source.GetExportRecordSP, Connection))
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add(new SqlParameter("#CandidateRecordID ", SqlDbType.NVarChar, 32)).Value = record;
try
{
if (Connection.State != ConnectionState.Open)
{
Connection.Open();
}
using (SqlDataReader reader = command.ExecuteReader())
{
if(reader.Read())
{
xmlRecord = new XmlDocument();
xmlRecord.LoadXml(reader.GetString(0));
}
}
}
catch (Exception Ex)
{
Logging.WriteError(string.Format("Error while retrieving the Record \"{0}\" details from Database. Exception: {1} ", Ex.ToString()));
throw;
}
}
Thanks in advance geeks.
Since you appear not to be using an OLEDB connection (which has an 8k limit), I think the problem is in your procedure code.
Or, perhaps, the compatibility version of your database is set to something other than SQL Server 2008 (SQL Server 2000 could not return more than 8k using GetString()).
Thanks for support, I found 1 fix for this at
http://www.sqlservercentral.com/Forums/Topic350590-145-1.aspx
Fix is, declare a variable, and should be initlized to empty string and concatenated with the main string.
DECLARE #test varchar(MAX);
set #test =''
select #test = #test + '<Invoice>.....'
If the string length is <8000 it will work without the above approach.
Thanks all.
I need to format a set of dates in SQL server to the following format..
yyyy-MM-ddThh:mm:ssZ
I cant seem to find how to format the date with the T and Z parts included in the string
Any ideas how to achieve this format in a SQL query?
According to the SQL Server 2005 books online page on Cast and Convert you use date format 127 - as per the example below
CONVERT(varchar(50), DateValueField, 127)
SQL Server 2000 documentation makes no reference to this format - perhaps it is only available from versions 2005 and up.
Note on the time zone added to the end (from note 7 in the docs): The optional time zone indicator, Z, is used to make it easier to map XML datetime values that have time zone information to SQL Server datetime values that have no time zone. Z is the indicator for time zone UTC-0. Other time zones are indicated with HH:MM offset in the + or - direction. For example: 2006-12-12T23:45:12-08:00.
Thanks to Martin for this note: You should be able to use STUFF to remove the miliseconds as these will be in a fixed position from the left of the string. i.e.
SELECT STUFF(CONVERT(VARCHAR(50),GETDATE(), 127) ,20,4,'')
DECLARE #SampleDate DATETIME2(3) = '2020-07-05 23:59:59';
SELECT CONVERT(VARCHAR(20), CONVERT(DATETIMEOFFSET, #SampleDate), 127);
--results: 2020-07-05T23:59:59Z
You can parse C# output in SQL using below:
SELECT CONVERT(DATETIME, CONVERT(DATETIMEOFFSET,'2017-10-27T10:44:46Z'))
Use C# to generate this using the following:
string ConnectionString = "Data Source=SERVERNAME; Initial Catalog=DATABASENAME; Persist Security Info=True; User ID=USERNAME; Password=PASSWORD";
using(SqlConnection conn = new SqlConnection(ConnectionString))
{
DateTime d = DateTime.Now;
string Query = "SELECT CONVERT(DATETIME, CONVERT(DATETIMEOFFSET,'" + d.ToString("yyyy-MM-dd") + "T" + d.ToString("HH:mm:ss") + "Z'))"
conn.Open();
using(SqlCommand cmd = new SqlCommand(Query, conn))
{
using(SqlDataReader rdr = cmd.ExecuteReader())
{
if(rdr.HasRows)
{
while(rdr.Read())
{
for(int i; i < rdr.length; i++)
{
Console.WriteLine(rdr[0].ToString());
}
}
//DataTable dt = new DataTable(); dt.Load(rdr); //Alternative method if DataTable preferred
}
}
}
}
select left(convert(varchar(30),getdate(),126)+ '.000',23)
Try this
SELECT STUFF(
CONVERT(datetime2(0), GETDATE(), 126)
AT TIME ZONE 'US Eastern Standard Time'
,11,1,'T')
on MSSQL
SELECT FORMAT( GETDATE(),'yyyy-MM-ddTHH:mm:ss.ms zzzz')