3

I have a DB table with two datetime nullable columns that I need to read from an ASP page using VBScript.

This is the code I wrote:

Set cmd = Server.CreateObject("ADODB.Command")
With cmd
    .ActiveConnection = conn
    .CommandType = adCmdText
    .Prepared = True
    .CommandText = "SELECT * FROM storico_corsi WHERE stc_id = 5 "
    Set rs = .Execute
    Response.Write("stc_scadenza = {" & rs("stc_scadenza") & "}, ")
    Response.Write("stc_inizio = {" & rs("stc_inizio") & "} ")
    If IsEmpty(rs("stc_inizio")) Then
        Response.Write("- ERROR!")
    End If
End With

This code gives this output:

stc_scadenza = {19/04/2014}, stc_inizio = {} - ERROR!

If I swap the order of retrieval to

...
    Response.Write("stc_inizio = {" & rs("stc_inizio") & "}, ")
    Response.Write("stc_scadenza = {" & rs("stc_scadenza") & "} ")
...

this is the (correct) result I get:

stc_inizio = {19/02/2014}, stc_scadenza = {19/04/2014} 

Why is a small change in the order of retrieval of elements from an ADO Recordset delivering a completely different outcome?

Please notice that I'm using the Italian locale (dd/mm/yyyy) and that stc_inizio and stc_scadenza, while being often set to midnight timestamps, are of SQL type datetime.


UPDATE #1: I made the code simpler and clearer by reducing the code to the manipulation of just two fields, adding the emptiness check and removing JSON stuff altogether. Some comments below refer to previous, more complicated, versions.


UPDATE #2: If I replace the SQL query with

SELECT stc_inizio, stc_scadenza FROM storico_corsi WHERE stc_id = 5

or

SELECT stc_scadenza, stc_inizio FROM storico_corsi WHERE stc_id = 5

it works properly! But why? This is the DB table I'm using:

CREATE TABLE [dbo].[storico_corsi] (
    [stc_id] [bigint] IDENTITY(1,1) NOT NULL,
    [stc_id_ute] [bigint] NOT NULL,
    [stc_utente] [varchar](100) NULL,
    [stc_anagrafica] [ntext] NULL,
    [stc_id_can] [bigint] NULL,
    [stc_canale] [varchar](500) NULL,
    [stc_FE_id] [bigint] NULL,
    [stc_quest_finale] [ntext] NULL,
    [stc_quest_corretto] [ntext] NULL,
    [stc_reg_fad] [ntext] NULL,
    [stc_inizio] [datetime] NULL,
    [stc_scadenza] [datetime] NULL,
    [stc_terminato] [char](1) NULL
        CONSTRAINT [DF_storico_corsi_stc_terminato_1]  DEFAULT ('N'),
    [stc_fine] [datetime] NULL,
    [stc_tempo] [bigint] NULL 
        CONSTRAINT [DF_storico_corsi_stc_tempo]  DEFAULT ((0)),
    [stc_data_in] [datetime] NULL 
        CONSTRAINT [DF_storico_corsi_stc_data_in_1]  DEFAULT (getdate()),
    [stc_progressivo] [int] NULL,
    [stc_anno] [int] NULL,
CONSTRAINT [PK_storico_corsi] PRIMARY KEY CLUSTERED ([stc_id] ASC) 
WITH (
    PAD_INDEX = OFF, 
    STATISTICS_NORECOMPUTE = OFF, 
    IGNORE_DUP_KEY = OFF, 
    ALLOW_ROW_LOCKS = ON, 
    ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) 
ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

UPDATE #3: The issue can be reproduced anytime I include any of the 3 ntext columns in the query. If I don't SELECT them, nothing bad happens. Apparently, ntext values break the record, but they also do that in an unpredictable way that depends on the order of retrieval of other fields.


UPDATE #4: BLOBs/ntexts have to be fetched last with ADO. This statement sounds a bit crazy but this is what I found here: http://p2p.wrox.com/sql-server-2000/3211-cant-pull-data-ntext-field-into-recordset.html#post78234 It's consistent with my experience.

Mauro Vanetti
  • 424
  • 1
  • 5
  • 18
  • 1
    Just to eliminate the obvious, is there definitely only one row in the database where `stc_id` is 5? – Matt Gibson Oct 07 '15 at 09:05
  • Yes. I checked right now. :-) – Mauro Vanetti Oct 07 '15 at 09:14
  • 1
    Okay. So, first thing to do is eliminate the possibility that it's that (custom?) jsArray class causing problems. If you just print out the value of `rs("stc_scadenza")` with your original code, does it have a value, or is it null? – Matt Gibson Oct 07 '15 at 09:16
  • Right approach. No, the problem is apparently not due to the JSON stuff. If I check `rs("stc_inizio")` with `IsEmpty`, it gives `True`. – Mauro Vanetti Oct 07 '15 at 09:40
  • And presumably if you use SQL Server Management Studio and do a `SELECT stc_inizio FROM storico_corsi WHERE stc_id = 5`, you see the expected result? – Matt Gibson Oct 07 '15 at 10:02
  • Yep, that's exactly what I did. I get _2014-02-19 00:00:00.000_ in the graphic tool. – Mauro Vanetti Oct 07 '15 at 10:03
  • 1
    Also, just as a sanity check, what values do you get if you change your original code to simply select a single row, e.g. `SELECT * FROM storico_corsi WHERE stc_id = 5`? That would eliminate any strange happenings from prior or subsequent loops... – Matt Gibson Oct 07 '15 at 10:04
  • 1
    Correct. Still the same. Actually I might update the question to remove extra stuff that gets in the way of understanding the core issue and make it easier to pin it down to the critical point, let me try. – Mauro Vanetti Oct 07 '15 at 10:06
  • OK, now it's really really simple. – Mauro Vanetti Oct 07 '15 at 10:32
  • 1
    And now it's really, really weird. Okay, last request: try to eliminate the use of your custom json object completely while still reproducing the problem. Because if you can't do that, then there's something quite odd going on in the code inside the json object, and we'll need to see that to figure things out. – Matt Gibson Oct 07 '15 at 10:41
  • It's getting crazy. :-) Check the last version, no JSON, no mess, just the plain madness of order-dependence. – Mauro Vanetti Oct 07 '15 at 10:53
  • 1
    Okay, one last sanity check. Do you still get the same result with `SELECT TOP (1) stc_scadenza, stc_inizio FROM storico_corsi WHERE stc_id = 5`? (Just checking that there's definitely only one result, plus wondering if selecting other columns with the "*" is confusing things, though I can't possibly see how it can be, really...) – Matt Gibson Oct 07 '15 at 10:59
  • The TOP part is not useful but explicitly listing the output columns fixed the issue. But I still have no rational explanation, check my update #2. – Mauro Vanetti Oct 07 '15 at 12:40
  • 1
    That's flat-out weird. Especially when selecting from a single table, where there can't be duplicate column names. I certainly can't explain it. Might even be an actual bug somewhere in the Microsoft code. Can you narrow it down to a column? i.e. can you reproduce it by selecting all the columns explicitly, `SELECT stc_id, stc_id_ute, stc_utente, ...`? And if you can, does it go away when you remove some of the columns? (I'd suspect the `ntext` ones, but that's only a hunch...) – Matt Gibson Oct 07 '15 at 12:48
  • 1
    You are right! Removing the 3 ntext columns, it works. I'm puzzled. – Mauro Vanetti Oct 07 '15 at 13:05
  • My guess would be that it's a bug when retrieving NTEXT column values in ADO. It may even be specifically related to the actual values in your NTEXT columns. It may be that if you update to later versions of whatever components you're using (ADO, .NET framework, etc.) that the problem simply disappears. I suspected the NTEXT values because they're the weirdest, i.e. the ones least often used in production systems, plus big and binary enough to show bugs (possibly memory overwrites) in the Microsoft code. – Matt Gibson Oct 07 '15 at 13:18
  • 1
    @MattGibson, you are definitely right, check my fourth (and hopefully last) update. Why don't you express this in an answer so that I can mark it as final and name you my hero? :-) – Mauro Vanetti Oct 07 '15 at 13:20

1 Answers1

2

Given the results you're seeing as we've investigated further, it seems as though the problem stems from the NTEXT columns that you're selecting implicitly with your SELECT * ... (but not using.)

As you've noted, there are occasional hints on the web that there at least used to be problems retrieving large binary fields earlier than the end of the SELECT list when using ADO. (As soon as you linked to that article I did have a vague memory from the dim and distant past that I'd seen that advice before too.)

I'd suspect you may be using an older version of ADO, and that this problem may have been fixed in later releases.

Given that in this particular case you don't actually want to retrieve the values from the NTEXT columns, you should simply limit your SELECT list to the values you actually need, and everything should work fine.

Note that it's generally considered best practice to avoid the use of SELECT * except for quick, ad-hoc queries. Explicitly selecting the columns you need has several benefits. For example, in this case, even if your SELECT had worked perfectly, you'd still have been retrieving the potentially large amounts of data in your NTEXT fields (possibly across the network) from your database only to discard it without using it...

Note also that in KB article 317016, "How To Read and Write BLOBs Using GetChunk and AppendChunk", there's some "suggestions for using BLOBs with ADO", including:

  • Select the BLOB columns last. Select individual fields, not "*".
Matt Gibson
  • 37,886
  • 9
  • 99
  • 128
  • 1
    Awesome investigation Matt! I also updated the title to make it easier for other users to find out about this nasty bug. I'm using SQL Server 12.0.2269 and ADO 10.0 on Windows 10. – Mauro Vanetti Oct 07 '15 at 13:40