Excel Microsoft Query parameter length limit - sql-server

MS Excel 2010 64-bit
Windows 7 Pro 64-bit on the local machine
SQL Server 2012
I often call stored procedures in SQL Server to return data to Excel for analysis in PivotTables. I pass parameters to the sp's using the following syntax in Microsoft Query's SQL Editor:
{Call dbo.MySP(?,?,?,)}
Where dbo.MySP is an SP that takes three parameters. When I return the output from the query to Excel I get to select which cells to refer to to get the parameter values.
Yesterday I wrote an SP that takes 3 arguments: a date, another date and a text string delimited by commas. The SP has them declared as two DATETIMES and an NVARCHAR(4000). As usual, I convert the dates in Excel to text strings in the form yyyy-mm-dd to pass them, but the text string is just a string of characters with individual values delimited by a pipe (|).
When I attempt to select the cell containing a text string less than 255 characters all is well. If I go over 255 I get an error from Excel complaining:
Bad parameter type. Microsoft Excel was expecting a different kind of
value that was was provided.
Changing the delimiter has no impact.
Is that a hard limit on the length a parameter passed from Excel can handle?
Is there a workaround of any sort?
Note that is is clearly an Excel limitation, running the same query with any length parameter works fine in SSMS.

Related

SQL Server appears to correctly interpret non-supported string literal formats

Have an instance of SQL Server 2012 that appears to correctly interpret string literal dates whose formats are not listed in the docs (though note these docs are for SQL Server 2017).
Eg. I have a TSV with a column of dates of the format %d-%b-%y (see https://devhints.io/datetime#date-1) which looks like "25-FEB-93". However, this throws type errors when trying to copy the data into the SQL Server table (via mssql-tools bcp binary). Yet, when testing on another table in SQL Server, I can do something like...
select top 10 * from account where BIRTHDATE > '25-FEB-93'
without any errors. All this, even though the given format is not listed in the docs for acceptable date formats and it apparently also can't be used as a castable string literal when writing in new records. Can anyone explain what is going on here?
the given format is not listed in the docs for acceptable date formats
That means it's not supported, and does not have documented behavior. There's lots of strings that under certain regional settings will convert due to quirks in the parsing implementation.
It's a performance-critical code path, and so the string formats are not rigorously validated on conversion. You're expected to ensure that the strings are in a supported format.
So you may need to load the column as a varchar(n) and then convert it. eg
declare #v varchar(200) = '25-FEB-93'
select convert(datetime,replace(#v,'-',' '),6)
Per the docs format 6 is dd mon YY, but note that this conversion "works" without replacing the - with , but that's an example of the behavior you observed.

SQL Server Datetime object persistent reformatting issue in Excel

I have an annoying issue working with SQL Server DATETIME objects in Excel 2013. The problem has been stated several times here in SO, and I know that the work around is to just reformat the DATETIME objects in Excel by doing this:
Right click the cell
Choose Format Cells
Choose Custom
In the Type: input field enter yyyy-mm-dd hh:mm:ss.000
This works fine BUT I loathe having to do this every time. Is there a permanent work around to this aside from creating macros? I need to maintain the granularity of the DATETIME object so I cannot use a SMALLDATETIME. I am currently using Microsoft SQL Server Management Studio 2008 r2 on a win7 machine.
Thanks in advance.
-Stelio K.
Without any code it's hard to guess how the data gets from SQL Server to Excel. I assume it's not through a data connection, because Excel wouldn't have any issues displaying the data as dates directly.
What about data connections?
Excel doesn't support any kind of formatting or any useful designer for that matter, when working with data connections only. That functionality is provided by Power Query or the PivotTable designer. Power Query is integrated in Excel 2016 and available as a download for Excel 2010+.
Why you need to format dates
Excel doesn't preserve type information. Everything is a string or number and its display is governed by the cell's format.
Dates are stored as decimals using the OLE Automation format - the integral part is the number of dates since 1900-01-01 and the fractional part is the time. This is why the System.DateTime has those FromOADate and ToOADate functions.
To create an Excel sheet with dates, you should set the cell format at the same time you generate the cell.
How to format cells
Doing this is relatively if you use the Open XML SDK or a library like EPPlus. The following example creates an Excel sheet from a list of customers:
static void Main(string[] args)
{
var customers = new[]
{
new Customer("A",DateTime.Now),
new Customer("B",DateTime.Today.AddDays(-1))
};
File.Delete("customers.xlsx");
var newFile = new FileInfo(#"customers.xlsx");
using (ExcelPackage pck = new ExcelPackage(newFile))
{
var ws = pck.Workbook.Worksheets.Add("Content");
// This format string *is* affected by the user locale!
// and so is "mm-dd-yy"!
ws.Column(2).Style.Numberformat.Format = "m/d/yy h:mm";
//That's all it needs to load the data
ws.Cells.LoadFromCollection(customers,true);
pck.Save();
}
}
The code uses the LoadFromCollection method to load a list of customers directly, without dealing with cells. true means that a header is generated.
There are equivalent methods to load data from other source: LoadFromDatatable, LoadFromDataReader, LoadFromText for CSV data and even LoadFromArrays for jagged object arrays.
The weird thing is that specifying the m/d/yy h:mm or mm-dd-yy format uses the user's locale for formatting, not the US format! That's because these formats are built-in into Excel and are treated as the locale-dependent formats. In the list of date formats they are shown with an asterisk, meaning they are affected by the user's locale.
The reason for this weirdness is that when Excel moved to the XML-based XLSX format 10 years ago, it preserved the quirks of the older XLS format for backward-compatibility reasons.
When EPPlus saves the xlsx file it detects them and stores a reference to the built-in format ID (22 and 14 respectively) instead of storing the entire format string.
Finding Format IDs
The list of standard format IDs is shown in the NumberingFormat element documentation page of the Open XML standard. Excel originally defined IDs 0 (General) through 49.
EPPlus doesn't allow setting the ID directly. It checks the format string and maps only the formats 0-49 as shown in the GetBfromBuildIdFromFormat method of ExcelNumberFormat. In order to get ID 22 we need to set the Format property to "m/d/yy h:mm"
Another trick is to check the stylesheets of an existing sheet. xlsx is a zipped package of XML files that can be opened with any decompression utility. The styles are stored in the xl\styles.xml file.

SSIS not exporting dates and money types to flat file as correctly formatted string

I am exporting a file that is going to be picked up by another system. To avoid rework in the other system I am trying to match an existing excel csv output exactly. I have a date column in the DB which I want to export as dd/mm/yyy. In the data flow task I have the following SQL as the source where I do the appropriate conversion. If I run this query in ssms I get the right output.
SELECT [Code]
,[Agency_Name]
,[Region_Group]
,CONVERT( varchar(20), [GrossAmtYrly] , 1) GrossAmtYrly
,CONVERT ( varchar(20), [SaleDate] , 103) SaleDate
,[MemberNo]
,[Surname]
,[Scale]
FROM [Land].[Sales]
I then link this to a flat file destination, the column that this is mapped to is set to DT_SR width 20 not text qualified.
But the output file is spitting out a date in format yyyy-mm-dd.
Similarly for the grossamtyrly the old excel generated csv had the amount with commas after each 3 digits, wrapped in ". The output column it is mapped to is DT_SR width 20 with text qualified to true.
The output file for that column is missing the commas for grossamtyrly.
So it seems like my conversions in the SQL are being ignored completely, but I can't work out why.
Thanks in advance for any suggestions!
Using SSIS 2012 - Visual Basic 2010, DB is SQL Server 2012
I'd use a derived column in the data flow to convert it to the format you want. If it's coming in as a text field in format yyyy-mm-dd, you can convert it to dd/mm/yyyy with the following expression:
SUBSTRING(dt,9,2) + "//" + SUBSTRING(dt,6,2) + "//" + SUBSTRING(dt,1,4)
Thanks Custodian, I figure out how to get it to work.
I double clicked on the flow arrow between the tasks and the metadata tab shows the data type of each column. When I first set this up I did access mode as table or view and so date and grossamt were set to DT_DATE and DT_CY, so I think SSIS was implictly converting the column back again to its original type.
Now I couldn't work out how to change them, So I deleted the DB Source and recreated it starting with the SQL Command option, and everything works as expected.

SQL Server Date Settings

We have recently moved some data from an SQL Database instance to another one in another location.
I seemed to have noticed that there are some facets of our old database instance where the date is passed as String to the SQL server and SQL server is able to parse it properly. For example, the application would simply pass a string value of "15/01/2010" and the database would immediately recognize it as 5th of January 2010. Is there a setting in SQL server which I need to turn on or modify cause right now, when I passed the same string value, what happens is that an error is being generated cause it cannot understand the string value passed as a date.
Thanks for your inputs.
try
SET DATEFORMAT dmy
but you should only use 'safe' formats when using strings
same formats are YYYYMMDD and yyyy-mm-ddThh:mi:ss.mmm (no spaces). It doesn't matter with these 2 formats what your language settings are
Take a look here: Setting a standard DateFormat for SQL Server
If you need to convert output use cast or convert
select convert(varchar(30),getdate(),101)
03/08/2010
select convert(varchar(30),getdate(),101) +' '
+ convert(varchar(30),getdate(),108)
03/08/2010 15:21:32

Executing stored procedures with date parameters: Command Object vs Connection Object

When supplying dates to a stored procedure via a parameter I'm a little confused over which format to use for the dates. My original VBA syntax used the ADO Connection object to execute the stored procedure:
Set SentDetailRS = Me.ADOConnectionToIntegrity.Execute("dbo.s_SelectAggregatedSentDetailList '" & fCSQLDate(EffectiveDate) & "'", , adCmdText)
This works fine for me using the date syntax yyyy-mm-dd but when another user executes the code they recieve the error: 13 'Type Mismatch'.
After some experimentation I found that supplying the date in the format dd/mm/yyyy fixes this error for the user but now gives me the error!
Executing the stored procedure using a command object with parameters works regardless of the format of the date (I assume ADO is taking care of the formatting behind the scenes). I thought that using the format yyyy-mm-dd would work universally with SQL Server?
I'm also perplexed as to why this problem appears to be user specific? I noticed that my default language on SQL Server is 'English' whereas the other user's default language is 'British English', could that cause the problem?
I'm using ADO 2.8 with Access 2003 and SQL Server 2000, SQL Server login is via Windows integrated security.
Be careful, and do not believe that ADO is taking care of the problem. Universal SQL date format is 'YYYYMMDD', while both SQL and ACCESS are influenced by the regional settings of the machine in the way they display dates and convert them in character strings.
Do not forget that Date separator is # in Access, while it is ' in SQL
My best advice will be to systematically convert your Access #MM-DD-YYYY# (or similar) into 'YYYYMMDD' before sending the instruction to your server. You could build a small function such as:
Public function SQLdateFormat(x_date) as string
SQLDateFormat = _
trim(str(datePart("yyyy",x_date))) & _
right(str(datePart("m",date)),2) & _
right(str(datePart("d",date)),2)
''be carefull, you might get something like '2008 9 3'
SQLDateFormat = replace(functionSQLDateFormat," ","0")
'' you will have the expected '20080903'
End function
If you do not programmatically build your INSERT/UPDATE string before sending it to the server, I will then advise you to turn the regional settings of all the machines to the regional settings of the machine hosting SQL. You might also have to check if there is a specific date format on your SQL server (I am not sure). Personnaly, I solved this kind of localisation problems (it also happens when coma is used as a decimal separator in French) or SQL specific characters problems (when quotes or double quotes are in a string) by retreating the SQL instructions before sending them to the server.
I would guess that fCSQLDate function is culture-specific - i.e. it will parse the date based on the user's locale settings. That's why you see the problem.
Anyway, using queries with concatenated strings is always a bad idea (injection attacks). You are better off if you use parameters.
Access uses # as date field delimiter. The format should be #mm/dd/yyyy# probably the #mm-dd-yyyy# will also work fine.
Sorry I don't know mysql, but with oracle I would always explicity state the format that I was expecting the format to be in, eg: 'DD-MM-YYYY', to avoid (regional) date format problems
Why not use the format
dd mmm yyyy
There is only one way it can be interpreted.
You can use the Date() function to return a universal date based on the machine date and time settings. The region settings on the machine will determine how it it formatted on the client end. If you leave the field as strictle a DateTime field then the cleint region settings can format the date.
Going into the server, using the Date() function should aslo work (returning a universal date value).
Also, use a command object and parameters in your query when you pass them to avoid SQL injection attacks on string fields.

Resources