ADO-Recorsets, MS SQL Server, OLEDB, ODBC, #-tables, and all this jazz...

qu-qu
Дата: 17.02.2001 03:22:37
Привет, всем...

Поковырялся я тут в "мелко-мягкой" базе знаний на предмет простого, казалось бы, вопроса:
"Почему не открывается рекодсет из ADO если та же самая SQL-процедура из QA возращает записи?"

И вот что родилось в итоге...
(для проведения "эксперимента" нужно иметь на SQL-сервере "демо"-базу "pubs" и сгенерить на ней простенькую процедурку:
if exists (select * from sysobjects where id=object_id('dbo.byroyalty1'))
drop procedure dbo.byroyalty1

GO

CREATE PROCEDURE byroyalty1 @percentage int
AS
--set nocount on
select au_id into #T from titleauthor
where titleauthor.royaltyper = @percentage
select * from #T
select au_id into #TT from titleauthor
where titleauthor.royaltyper Between @percentage and 100
select * from #TT
drop table #T
drop table #TT
)

Далее - просто скопируйте лежащий ниже текст в любой файлик с расширением .ASP, исправьте в нем имя сервера БД, логин и пароль в строке коннекта (если понадобится), запускайте броузер с УРЛ-ом вроде "http://localhost/my_something.asp" и удивляйтесь... (как это делал я... пояснения будут ниже).

<% @ LANGUAGE="VBSCRIPT" %>
<% Option Explicit %>

<!--#include file="adovbs.inc"-->

<HTML>
<HEAD>
<TITLE>Using Stored Procedures with #-tables</TITLE>
</HEAD>

<BODY bgcolor="white" topmargin="10" leftmargin="10">

<!-- Display Header -->


<%

Dim oConn
Dim rstCompound
Dim intCount
Dim Ar_strConn(1)
Dim ttt

Ar_strConn(0)="SQLOLEDB"
Ar_strConn(1)="MSDASQL"

Set oConn = Server.CreateObject("ADODB.Connection")

For Each ttt In Ar_strConn
%>
<font size="4" face="Arial, Helvetica">
<b>Using <% If ttt="SQLOLEDB" Then %>
OLEBD <%
Else %>
ODBC <%
End If %></b></font><p>
<%
With oConn
.Provider = ttt
.ConnectionString = "server=HOMEALONE;uid=sa;pwd=;database=pubs;"
If ttt="MSDASQL" Then .ConnectionString = "Driver={SQL Server};" & .ConnectionString
.Open
End With


Response.Write oConn.Provider & "<br>"



' Open compound recordset.

Set rstCompound = Server.CreateObject("ADODB.Recordset")
rstCompound.Open "exec byroyalty1 75", oConn, , , adCmdText

' Display results from each T-SQL statement.
intCount = 1
Do Until rstCompound Is Nothing
Response.Write "Step " & intCount & "<br>"
Response.Write "Contents of recordset #" & intCount & "<br>"
If rstCompound.State = 1 Then
Do While Not rstCompound.EOF
Response.Write "Author ID = " & rstCompound.Fields(0) & "<br>"
rstCompound.MoveNext
Loop
End If

Set rstCompound = rstCompound.NextRecordset
intCount = intCount 1
Loop

oConn.Close

Next
%>

</BODY>
</HTML>

-------------------------------------------------------------------------------------

К чему я все это "затеял"?
Для тех, кто "в танке" - поясняю: оказывается ADODB.Recordset ведет себя совершенно по-разному с различными "провайдерами" соединения (ADODB.Connection).
Для ODBC - передаются клиенту ТОЛЬКО наборы записей, которые могут возвращать строчки...
Для OLEDB - передаются клиенту ВСЕ "наборы записей", независимо от того - ждут от них строчек, или не ждут...
(частный случай - для операции "UPDATE ... WHERE ..." будет передано "(n row(s) affected)")

Дальше уже начинается - именно сам "jazz" (и именно с OLEDB):
1. Если результат работы SQL-комманды ДОЛЖЕН возвращать набор записей, то ADODB.Recordset будет ОТКРЫТ, и покажет все эти записи при их наличии (при отсутствии таковых свойства ADODB.Recordset.EOF и ADODB.Recordset.BOF одновременно будут равны True, и ADODB.Recordset будет ПУСТ);
2. Если результат работы SQL-комманды НЕ ДОЛЖЕН возвращать набор записей (например, UPDATE ..., INSERT ..., DELETE ...), то ADODB.Recordset НЕ будет ОТКРЫТ НИКОГДА, и свойства ADODB.Recordset.EOF и ADODB.Recordset.BOF одновременно НЕ будут существовать ВООБЩЕ (нет таких свойств у закрытого объекта);
3. Если результат работы SQL-комманды НЕ ПЕРЕДАЕТСЯ клиенту вообще (например, DROP PROCEDURE ... , DROP TABLE ...), то ADODB.Recordset становится НЕСУЩЕСТВУЮЩИМ, т.е. равным Nothing (прикольно, не правда ли?).

З.Ы. В начале SQL-процедуры, которая используется в этом примере, не зря указана закомментированная директива "SET NOCOUNT ON", если вы ее раскомментируете и перегенерите процедуру, то быстро убедитесь, что "неоткрытые" ADODB.Recordset у OLEDB-соединения сразу же "исчезнут", останутся только "значимые" (как и у ODBC).

З.З.Ы. Самое прикольное, что "мелкомягкие" это за "баг" не считают, а пишут об этом - как о "фиче" периода разработки... (типа - сами разбирайтесь что, как и зачем вы пишете в своем "гребанном" коде).

А между тем, все это корректно "отрабатывало" у меня только на связке - ADO2.5 SQL7.0, а вот Oleg попробовал связку ADO2.6 SQL2000 и "наступил на грабли" - судя по его постингу (http://sql.ru/cgi-bin/UltraBoard/UltraBoard.pl?Action=ShowPost&Board=mssql&Post=508&Idle=365&Sort=0&Order=Descend&Page=0&Session=) - не все так уж там гладко...

Удачи всем!!
Дед Маздай
Дата: 18.02.2001 00:35:51
Все ругательства пред.сооб-я сводятся к простому правилу - содержимое закладки Messages в Query Analyzer'e, не относящееся к операции вывода данных (select), выводится в виде отдельных Recordsetов. Которые, разумеется, имеют BOF и EOF выставленными в True, п.ч. записей не содержат. Для иллюстрации этого можно обойтись существенно меньшим кол-вом кода:

Set cn = CreateObject("ADODB.Connection")
cn.Open "Provider=SQLOLEDB;Data Source=alexeysh-lpt\Leshik1;Initial Catalog=pubs;Integrated Security=SSPI"
Set rs = CreateObject("ADODB.Recordset")
rs.Open "exec byroyalty1 75", cn, , , adCmdText
nRSCount = 0
While Not rs Is Nothing
nRSCount = nRSCount + 1
Debug.Print nRSCount
If rs.State <> 0 Then Debug.Print rs.GetString(adClipString)
Set rs = rs.NextRecordset
Wend
cn.Close
, который покажет ровно 4 рекордсета (соотв-но, 2 при включенном set nocount on, которая отрубает messages). Это уже обсуждалось в данной конференции, а еще раньше было описано в документации (http://support.microsoft.com/support/kb/articles/Q245/1/79.ASP, http://support.microsoft.com/support/kb/articles/Q197/5/28.ASP). Если бы qu-qu на с.д. внимательно поковырялся в мелкомягкой базе знаний, то наверняка бы обнаружил, что этим жизнь не ограничивается. Напр., итоговая строка compute by тоже возвращается отдельным recordset'ом (http://support.microsoft.com/support/kb/articles/Q194/0/05.ASP и т.д. Можно долго обсуждать, насколько такое поведение удобно или неудобно, но по крайней мере не стоит раздувать из этого открытие вселенского масштаба.

PS. Мелкомягким, безусловно, глубоко накласть на то, что пишет qu-qu в своем гребаном коде, тем не менее, по некоторым вопросам они решили пойти ему навстречу в ADO .NET
qu-qu
Дата: 19.02.2001 10:12:52
2 Дедушке Маздаю...

С чего ты взял, что в моем "корневом" сообщении была "ругань" на что либо? Или ты слово "гребанный" можешь использовать только адресно, пытаясь оскорбить конкретного собеседника, который тебе чем-то не понравился?

Ну да, из тех 3-х ссылок, которые ты привел в качестве доказательства своей крутости - я действительно "до этого" видел только 1-ну, но тем не менее мне хватило и 1-й, чтобы понять что на самом деле не все так просто и гладко, как написано в мелкомягких "HOWTO:" (и твои же слова - лишнее тому подтверждение).

Не хотел я никого ругать, а хотел донести до людей, которые по каким-то причинам не могут сами разобраться в этом вопросе, что "открытие" рекордсетов в ADO имеет свои "подводные камни"...

Ну да, было уже обсуждение подобных проблем в этой конфе (и еще в 100 других конфах тоже), ну и что из этого? Кому это помогло? Только тем, кто интересовался этим обсуждением раньше, кому оно "попалось на глаза" в свое время. Вся беда подобный конференций как раз в том и заключается, что одни и те же вопросы "обсасываются" в них по кругу бесконечное количество раз, т.к. всегда проще задать свой вопрос, чем перелопачивать килотонны "мусора" в надежде найти то, что уже когда обсуждалось... (да и как, например, определить с первого взгляда, что в ветке с названием "Передача параметров хранимой процедуре" обсуждается открытие множественных рекордсетов?).

Так что в любом случае - ты многое неправильно понял: нет у меня проблем с открытием множественных рекордсетов, не ругался я ни на кого, и не имеет рекордсет полученный после исполнения команды UPDATE свойств EOF и BOF по той простой причине, что рекордсет этот закрыт, а закрытые обьекты многих свойств не имеют...

Вот такой, простой казалось бы, вывод - даже для тебя, обсуждавшего уже раньше эту проблему и смотрящего внимательно на сайты мелкомягких, остается как бы в тумане... (что-то слышал, что-то видел, у меня работает, но до конца не понял - как).

>содержимое закладки Messages в Query Analyzer'e, не относящееся к операции вывода данных (select),
>выводится в виде отдельных Recordsetов.
>Которые, разумеется, имеют BOF и EOF выставленными в True, п.ч. записей не содержат.

Ты сам себе противоречишь, т.к. в своем примере кода используешь для отсечения "реального" рекордсета от "нереального" проверку свойства State, а ты попробуй проверять в том же месте BOF и EOF на True, а я посмотрю - с каким треском ADO тебя пошлет... (и будет право).
VILARik
Дата: 19.02.2001 19:51:25
Не потеме замечание,

Спасибо qu-qu, что не донес до народа!

А вот, у Деда Маздай очень уж тон знакомый, может я и ошибаюсь но в строке

n.Open "Provider=SQLOLEDB;Data Source=alexeysh-lpt\Leshik1;Initial Catalog=pubs;Integrated Security=SSPI"

alexeysh очень напоминает "Алексей Шуленин", а именование Leshik должно быть всем знакомо, кто посещает MS семинары по SQL.

Алексей
VILARik
Дата: 19.02.2001 19:57:53
Opsss описался

В строке "Спасибо qu-qu, что не донес до народа!"
Хотел сказать:
"Спасибо qu-qu, что не поленился донести до народа!" а коротко "Спасибо qu-qu, что донес до народа!" Вот и скомпилировал :о)

Алексей
VILARik
Дата: 19.02.2001 20:35:06
Ой, что то меня понесло

Вот недавняя ветка,

http://www.sql.ru/cgi-bin/UltraBoard/UltraBoard.pl?Action=ShowPost&Board=mssql&Post=493&Idle=365&Sort=0&Order=Descend&Page=1&Session=

там и Деда Маздай высказался в своем стиле, :о)

а вот ответ:
http://www.interface.ru/fset.asp?Url=/microsoft/news/m010205183.htm

кто там у нас автор? :о)

Алексей
Психолог
Дата: 20.02.2001 19:38:38
Да нет, нормальный дед. Только наверное борьбой или боксом в детстве не занимался... Вот теперь неисчерпанная инергия и исчет выход. Только с возрастом будет все больней и больней...
Garya
Дата: 20.02.2001 19:41:43
Ребята, давайте не будем ссориться. Дедушка Маздай, лично я отношусь с огромным уважением к Вашим знаниям, однако прошу Вас быть несколько мягче в высказываниях.
Небольшая реплика к сказанному. В проектах Access-2000 фраза "set nocount on" предлагается в шаблоне вновь создаваемой хранимой процедуры. И, как показывает опыт, не зря...
Терапевт
Дата: 21.02.2001 10:00:30
Не надо бояться эмоций. Нет ничего ужасного в том, что виртуальный дедешка наезжает на виртуального гу-гу. И ругонь у них виртуальная. Главное палку не перегибать... С первых дней своего бытия интернет страдал некоторым утрированием и фомильярностью. Иногда это полезно (многим из нас нужен хороший "пинок", что бы сдвинутся со своих ложных убеждений), а иногда не очень... Если кто - либо "зарвётся", это больше проблема модератора, а не посетителей конференции.
SergSuper
Дата: 21.02.2001 11:13:19
Offtopic:
Как-то я решил найти Деда Маздая в Аське, их там было 4, судя по всему нашего там не было, но я посмеялся прочитав Info/About у 58104600. Так что среди Маздаев наш не самый ... маздаистый