Отзывы
и оценки |
|
sysadm2000 |
26 августа 2005, 13:49 |
Оценка: N/A |
не та основная проблема во втором примере,
что указал автор FAQ Основная проблема - что НАДО
ТОЧНО ЗНАТЬ структуру возвращаемого рекордсета... А
кто бы его знал... Если его знать - то зачем бы вообще
возникала эта задача... |
|
Веселов Константин Евгеньевич |
26 марта 2005, 17:44 |
Оценка: 3 |
Вот еще вариант:
EXEC
sp_addlinkedserver '(local)';
SELECT * FROM
OPENQUERY([(local)], 'EXEC dbo.dataset_sp') |
|
Гавриленко Сергей Алексеевич |
05 марта 2005, 12:22 |
Оценка: 4 |
Сам же правлюсь: вместо "таким образом
можно получить только первый набор данных" следует
читать "Таким образом можно получить все наборы данных,
возвращаемые процедурой, если они соответствуют
структуре таблицы; если хоть один набор не
соответствует, будет выдана соответствующая
ошибка." |
|
|
|
Автор:
BPMargolin, Neil Pike Прислал: cat2
|
|
==================== Q. How can I code a dynamic
varying ORDER BY statement in SQL Server?
A.
First let's illustrate the concept ...
Using the
"pubs" database that ships with SQL Server, let's say
that we want to create a stored procedure that will
allow the user to select the entire "authors" table
sorted by "au_id", the author name, or ZIP code. To make
it a bit more interesting, let's also support!the option
of sorting the author's name either first name first, or
last name first.
So, in essence, we want to
program a stored procedure that via, the value of a
single input parameter, will enable any of the following
four result sets to be generated. In short, we want to
be able to execute...
EXEC spAuthors 1
or
EXEC spAuthors 2
or
EXEC spAuthors 3
or
EXEC spAuthors 4 | | and
get ... Result set #1 - sorted by author
identification code ("au_id"): The code we will want
to be executed is:
SELECT au_id, au_fname, au_lname
FROM authors
ORDER BY au_id | | and
this will create the following output:
au_id
au_fname au_lname ----------- -------- --------
172-32-1176 Johnson White 213-46-8915 Marjorie
Green 238-95-7766 Cheryl Carson
. . .
172-32-1176 Johnson White 213-46-8915
Marjorie Green 238-95-7766 Cheryl Carson
. .
.
893-72-1158 Heather McBadden 899-46-2035
Anne Ringer 998-72-3567 Albert Ringer
Result set #2 - sorted by author's name,
first name first: The code should be:
SELECT au_id, au_fname, au_lname
FROM authors
ORDER BY au_fname, au_lname | | which
will produce:
au_id au_fname au_lname
----------- -------- -------- 409-56-7008
Abraham Bennet 672-71-3249 Akiko Yokomoto
998-72-3567 Albert Ringer
. . .
846-92-7186 Sheryl Hunter 724-80-9391
Stearns MacFeather 807-91-6654 Sylvia Panteley
Result set #3 - sorted by author's name,
last name first: The code will be:
SELECT au_id, au_fname, au_lname
FROM authors
ORDER BY au_lname, au_fname | | This
code generates:
au_id au_fname au_lname
----------- -------- -------- 409-56-7008
Abraham Bennet 648-92-1872 Reginald Blotchet-Halls
238-95-7766 Cheryl Carson
. . .
724-08-9931 Dirk Stringer 172-32-1176
Johnson White 672-71-3249 Akiko Yokomoto
And
finally, result set #4 - sorted by ZIP code: The
code will be:
SELECT au_id, au_fname, au_lname
FROM authors
ORDER BY zip | | With
a result set of:
au_id au_fname au_lname
----------- -------- -------- 807-91-6654 Sylvia
Panteley 527-72-3246 Morningstar Greene
722-51-5454 Michel DeFrance
. . .
472-27-2349 Burt Gringlesby 893-72-1158
Heather McBadden 648-92-1872 Reginald Blotchet-Halls
Okay, now that we have a firm idea of
what we're looking for, let's see how we can go about
creating a stored procedure with the flexibility we
want.
Our coding options include:
I.
Using IF ... THEN ... ELSE to execute one of four
pre-programmed queries, II. Constructing the SQL
statements dynamically, and using either the EXECUTE()
function or sp_executesql system stored procedure to
execute it, III. Using a CASE statement to choose
the sequencing, IV. Using ANSI SQL-92 standard code
suggested by renowned SQL Guru Joe Celko, and V.
Using ANSI SQL-99 (SQL-3) code originated by the very
gifted Richard Romley.
Option I is what
probably first comes to mind to most individuals. The
stored procedure would probably look something like:
USE pubs
GO
CREATE PROCEDURE dbo.spAuthors
@OrdSeq tinyint
AS
IF @OrdSeq = 1
BEGIN
SELECT au_id, au_fname, au_lname
FROM authors
ORDER BY au_id
END
ELSE IF @OrdSeq = 2
BEGIN
SELECT au_id, au_fname, au_lname
FROM authors
ORDER BY au_fname, au_lname
END
ELSE IF @OrdSeq = 3
BEGIN
SELECT au_id, au_fname, au_lname
FROM authors
ORDER BY au_lname, au_fname
END
ELSE IF @OrdSeq = 4
BEGIN
SELECT au_id, au_fname, au_lname
FROM authors
ORDER BY zip
END
GO | | Each
option has its advantages and disadvantages, so let's
begin by critiquing this one.
The advantages
include: a) the code is straightforward, and easy to
understand, and b) the SQL Server query optimizer is
able to create an optimized query plan for each SELECT
query, thus ensuring maximal performance.
The
primary disadvantage is that there are four separate
SELECT queries that have to be maintained should the
reporting requirements change.
Option II
is an alternative that will also be frequently
suggested, particularly by those with experience with
using dynamic queries in SQL Server.
USE pubs
GO
CREATE PROCEDURE dbo.spAuthors
@OrdSeq tinyint
AS
DECLARE @SQLstmt varchar (255)
SELECT @SQLstmt = 'SELECT au_id, '
SELECT @SQLstmt = @SQLstmt + 'au_fname, '
SELECT @SQLstmt = @SQLstmt + 'au_lname '
SELECT @SQLstmt = @SQLstmt + 'FROM authors '
SELECT @SQLstmt = @SQLstmt +
CASE @OrdSeq
WHEN 1 THEN 'ORDER BY au_id'
WHEN 2 THEN 'ORDER BY au_fname, au_lname'
WHEN 3 THEN 'ORDER BY au_lname, au_fname'
WHEN 4 THEN 'ORDER BY zip'
END
EXEC (@SQLstmt)
GO | | Note
that in SQL Server 7.0, you can use the system stored
procedure sp_executesql in place of the EXEC() function.
Please refer to the SQL Server Books Online for the
advantages of sp_executesql over the EXEC() function.
While this is a perfectly good option, it does
have two significant disadvantages. Perhaps the more
important of the two is, that the user of the stored
procedure must have appropriate permissions on any
database objects referred to inside EXEC() or
sp_executesql, in addition to an EXECUTE privilege on
the stored procedure itself.
Also, another
possible disadvantage of this coding is that the SELECT
statement, when placed inside the EXEC() function is not
cached. Thus every invocation of the spAuthor stored
procedure, when coded with a call to the EXEC()
function, will result in SQL Server re-parsing the
SELECT code, and generating a query plan anew. This is
probably not a concern in most production environments,
but it might be of importance in a high-performance OLTP
shop. (Note that sp_executesql will cache the query
plans.)
Option III has garnered some
support!on the Microsoft SQL Server newsgroups since it
was first offered, although I believe that in practice
it is perhaps the least flexibility of the 5 options
being presented here. Nevertheless, it does lead us away
from the EXEC() function and/or sp_executesql.
USE pubs
GO
CREATE PROCEDURE dbo.spAuthors
@OrdSeq tinyint
AS
SELECT au_id, au_fname, au_lname
FROM authors
ORDER BY CASE @OrdSeq
WHEN 1 THEN au_id
WHEN 2 THEN au_fname + ' ' + au_lname
WHEN 3 THEN au_lname + ' ' + au_fname
WHEN 4 THEN zip
ELSE NULL
END
GO | | It
is easy to see why this is a very popular solution. At
first glance, it seems to be the ideal solution. However
it does suffer from one very serious flaw. The CASE
construction evaluates to value of a specific data type.
In this case, all four columns ... au_id, au_fname,
au_lname and zip ... are character strings, and SQL
Server will, when parsing the statement, look at the
expressions after the THEN clause and construct a data
type that can hold, without lose of precision, any of
the individual expressions. In fact, the data type
returned by the CASE construction in the above code will
be varchar (61).
However, this technique just
won't stand up to the demands of sequencing by columns
of significantly different data types.
To see
how fragile the code actually is, add before the WHEN 1
clause, the following:
WHEN 0 THEN 6.44
and use the following to call the stored
procedure ...
EXEC dbo.spAuthors 1
The technique offered in Option IV
was first posted by well-known SQL Guru Joe Celko in
response to the technique in Option III. Joe
participated in the creation of the ANSI SQL-92
standard, and thus is a strong supporter of ANSI SQL-92
compliant code. Joe frequently makes the point that code
written to the SQL-92 standard is portable to any
database that supports the standard.
USE pubs
GO
CREATE PROCEDURE dbo.spAuthors
@OrdSeq tinyint
AS
SELECT au_id, au_fname, au_lname,
CASE @OrdSeq
WHEN 1 THEN au_id
WHEN 2 THEN au_fname + ' ' + au_lname
WHEN 3 THEN au_lname + ' ' + au_fname
WHEN 4 THEN zip
ELSE NULL
END AS OrdSeq
FROM authors
ORDER BY OrdSeq
GO | | Note
that this code does require an additional column
(OrdSeq) in the result set so that the ORDER BY clause
has something to "work" on. When Joe Celko posted the
technique, there was criticism concerning the additional
column. I'd offer as a thought that stored procedures
should be invoked not by end-users, but by applications.
The application can just ignore the "extraneous" column.
Nevertheless, additional bytes are being pushed across a
network, and that, thus, can be a performance
consideration. It can also be argued that we have
changed the definition of the problem to accommodate
this solution. Nevertheless, I agree with Joe Celko,
that, if portability of code is important, this solution
is definitely worth considering.
The careful
reader might notice that the columns au_id, au_fname,
au_lname and zip are all character strings, and might
therefore conclude that the technique works only when
with columns of similar data types. As Joe Celko pointed
out however, the ANSI SQL-92 standard also supports the
CAST function to transform one data type into another.
Since all of the common data types are ultimately human
readable, they can be converted into an alphanumeric
format, and thus columns of the various numeric data
types can also be used along with character string data
types. (The CAST function was introduced into SQL Server
beginning with version 7.0. A SQL Server 6.5 solution
would have to use the well-known CONVERT function.)
The authors table in pubs does not contain a
column with a strictly numeric data type, so it is a bit
difficult to illustrate. Let us assume however that the
"zip" column in authors is actually defined as an
integer data type rather than as char(5). In that case,
the SELECT could be programmed ...
SELECT au_id, au_fname, au_lname,
CASE @OrdSeq
WHEN 1 THEN au_id
WHEN 2 THEN au_fname + ' ' + au_lname
WHEN 3 THEN au_lname + ' ' + au_fname
WHEN 4 THEN RIGHT ('00000' + CAST (zip as char(5)), 5)
ELSE NULL
END AS OrderingSequence
FROM authors
ORDER BY OrderingSequence | | In
order for the sorting to work properly, we do have to be
aware of, and take into account, the format of the
output from the CAST function. In SQL Server, you can
experiment and see for yourself that integer values cast
to character format will result in left-aligned output.
This will sort incorrectly, so we have to force a
right-alignment. Because the ANSI SQL-92 standard is
weak on string manipulation functions, we are forced to
rely upon the SQL Server-specific RIGHT function to
achieve this, thus breaking the portability of the code.
The last piece of code, Option V, was
originally posted by the very gifted Richard Romley. It
is not ANSI SQL-92 compliant, but just might be with the
SQL-99 (aka SQL-3) standard. It is my personal favorite.
USE pubs
GO
CREATE PROCEDURE dbo.spAuthors
@OrdSeq tinyint
AS
SELECT au_id, au_fname, au_lname
FROM authors
ORDER BY
CASE @OrdSeq WHEN 1 THEN au_id ELSE NULL END,
CASE @OrdSeq WHEN 2 THEN au_fname + ' ' + au_lname ELSE NULL END,
CASE @OrdSeq WHEN 3 THEN au_lname + ' ' + au_fname ELSE NULL END,
CASE @OrdSeq WHEN 4 THEN zip ELSE NULL END
GO | | There
are many similarities between this code and the code
presented in options III and IV. However, Richard Romley
has avoided the problems inherent with the fact that
CASE can only return a value of one specific data type
by breaking the ORDER BY into four separate CASE
expressions. Using this construction, SQL Server can
return an appropriate data type for each CASE
expression, without ever getting tangled up trying to
transform data types.
By the way, the reason
that this solution is not SQL-92 compliant is because
the SQL-92 standard only permits ORDER BYs using a
column, and not an expression. SQL Server has long
supported ORDER BYs using an expression, and the SQL-99
appears to be ready to accept that extension.
v2.02 2000.06.03 Applies to SQL Server
versions : 6.5, 7.0, 2000 FAQ Categories :
Application Design and Programming Authors :
BPMargolin, Neil Pike
| |
|
|
Автор:
Glory Прислал: Glory
|
|
Наверное, одним из первых вопросов, возникающих у
начинающих программистов на T-SQL, это вопрос "А как
получить выборку из таблицы, имя которой занесено в
переменную ?" Т.к. в T-SQL нет возможности
использовать в некоторых частях запроса значения
переменных, то единственным доступным решением является
использование динамического запроса. Идея очень проста:
в специально определнной переменной "собирается" строка
запроса, которая должна выполняться. Далее эта строка
запускается на выполнение. Запуск можно осуществить
двумя способами - с помощью команды EXECUTE - с
помощью системной процедуры
sp_executesql.
Выглядит это приблизительно
так
DECLARE @SQL varchar(8000), @table_name varchar(10)
SET @SQL = 'SELECT * FROM ' + @TableName
exec(@SQL)
--или
exec sp_executesql @SQL | Обычно
динамические запроса формируются внутри хранимых
процедур, в которых по входным параметром составляется
конкретная строка выполнения.
I.Особенности
динамического запроса 1. Динамический запрос ВСЕГДА
выполняется В ТОМ-ЖЕ КОННЕКТЕ и КАК ОТДЕЛЬНЫЙ
ПАКЕТ(batch). Другими словами при использовании такого
запроса, - вы ни имеете доступа к локальным
переменным, объявленым до вызова динамического запроса
(однако возможен доступ к объявленным ранее временным
таблицам) - все временые таблицы и переменные,
созданные во время выполнения команды exec, будут
недоступны в вызывающей процедуре, т.к. будут удалены по
окнончании пакета, открытого для exec.
2.
Динамический запрос ВСЕГДА выполняется с ПРАВАМИ
ПОЛЬЗОВАТЕЛЯ, ВЫЗВАВШЕГО ПРОЦЕДУРУ, а не с правами
владельца процедуры. Другими словами, если владельцем
процедуры Procedure1 является User1, который имеет права
к таблице Table1, то для пользователя User2 мало
назначить права на выполнение процедуры Procedure1, если
обращение в ней к таблице Table1 идет через динамический
запрос. Придется давать ему соответствующие права и
непосредственно для Table1.
3. Компиляция запроса
происходят непосредственно перед его вызовом. Т.е. обо
все синтаксических ошибках вы узнаете только в этот
момент.
II.Особенности использования команда
exec 1. Команда exec поддерживает к качестве
аргумента конкатенацю строк и/или переменных. НО не
поддерживатеся конкатенация результатов выполнения
функций, т.е. конструкции вида
exec('SELECT * FROM ' + LEFT(@TableName, 10)) | запрещены
к использованию. 2. В команде нет входных/выходных
параметров.
III.Особенности использования
процедуры sp_executesql 1. Процедура НЕ поддерживает
в качестве параметров конкатенацию строк и/или
переменных. 2. Текст запроса должен быть либо
переменной типа NVARCHAR/NCHAR, либо такого же типа
стринговой константой. 3. Имеется возможность
передачи параметров в выполняемый скрипт и получение
выходных значений Последнее явно в документации не
описано, поэтому вот несколько примеров
В данном
примере в динамический запрос передаются 4 переменные,
три из которых яляюся выходными
declare @var1 int, @var2 varchar(100), @var3 varchar(100), @var4 int
declare @mysql nvarchar(4000)
set @mysql = 'set @var1 = @var1 + @var4; set @var2 = ''CCCC''; set @var3 = @var3 + ''dddd'''
set @var1 = 0
set @var2 = 'BBBB'
set @var3 = 'AAAA'
set @var4 = 10
select @var1, @var2, @var3
exec sp_executesql @mysql, N'@var1 int out, @var2 varchar(100) out, @var3 varchar(100) out, @var4 int',
@var1 = @var1 out, @var2 = @var2 out, @var3 = @var3 out, @var4 = @var4
select @var1, @var2, @var3 |
В
данном примере в динамическом запросе открывается
курсор, который доступен в вызывающей процедуре через
выходную переменную
USE pubs
declare @cur cursor
exec sp_executesql N'set @curvar= cursor local for select top 10 au_id, au_lname, au_fname from authors open @curvar' ,
N'@curvar cursor output ', @curvar=@cur output
FETCH NEXT FROM @cur
WHILE @@FETCH_STATUS = 0
BEGIN
FETCH NEXT FROM @cur
END |
Резюме(более
IMHO, чем обязательные требования) Динамический
запрос очень полезная и иногда просто незаменимая вещь,
НО способы его реализации и конкретно через вызов в
отдельном пакете с правами пользователя, вызвавшего
процедуру, принижают его практическое МАССОВОЕ
применение. Glory
| |
|
|
Автор:
MiCe Прислал:
|
|
Создание Linked server
EXEC sp_addlinkedserver
@server = 'FOX_ODBC',
@provider = 'MSDASQL',
@srvproduct = '',
@provstr = 'Driver={Microsoft Visual FoxPro Driver};
UID=;SourceDB=C:\;SourceType=DBF;Exclusive=No;BackgroundFetch=Yes;Col
late=Russian;Null=No;Deleted=No'
EXEC sp_addlinkedserver
@server = 'FOX_OLEDB',
@provider = 'VFPOLEDB',
@srvproduct = '',
@datasrc ='z:\',
@provstr = 'Collating Sequence=RUSSIAN' | Примеры
запросов
select * from FOX_OLEDB...[db\medbf] -- относительно datasrc
select * from FOX_OLEDB...[\\srv\share\db\medbf] -- UNC
select * from FOX_OLEDB...[c:\db\medbf] -- local path | ---------------------------
Так
можно создавать и модифицировать dbf файлы.
select *
from OPENQUERY(FOXODBC,
'select * from \\srv\buh\existdbf ;create dbf \\srv\buh\newdbf (id c(10),name c(50)) '
) a
-- файл \\srv\buh\existdbf - пустышка!!!! но он должен существовать
-- лично я использую специально созданный для этого пустой dbf
-- собственно создание пррисзодит во второй команде
| Идея состоит в том,
чтобы "под прикрытием" запроса к фиктивной таблице
передать Linked server-у на выполнение другие
инструкции.
PS.. вот перечень команд которые
поддерживает 'VFPOLEDB'( для ODBC for Visual FoxPro
некоторые не поддерживаются).... выдержка из из
"хелпа" к VS .Net
7.0 -------------------------------------------- The
Visual FoxPro OLE DB Provider supports the native Visual
FoxPro language syntax for the following commands:
CREATE TABLE - SQL Command Creates a table
having the specified fields. DELETE - SQL Command
Marks records for deletion. DROP TABLE Command
Removes a table from the database specified with the
data source and deletes it from disk. INSERT - SQL
Command Appends a record to the end of a table that
contains the specified field values. SELECT - SQL
Command Retrieves data from one or more tables.
UPDATE - SQL Command Updates records in a table
with new values. The Visual FoxPro Language
Reference contains detailed information about the
following supported commands:
ALTER TABLE - SQL
Command Programmatically modifies the structure of a
table. CREATE TABLE - SQL Command Creates a
table having the specified fields. Using Data
Definition Language (DDL) You cannot include DDL in
the following places:
In a batch SQL statement
that requires a transaction Following a previously
executed statement that required a transaction if not in
auto-commit mode and if your application has not yet
called SQLTransact. For example, if you want to
create a temporary table, you should create the table
before you begin the statement requiring a transaction.
If you include the CREATE TABLE statement in a batch SQL
statement that requires a transaction, the provider
returns an error message.
DELETE - SQL Command
Marks records for deletion. DELETE TAG Command
Removes a tag or tags from a compound index (.cdx)
file. DROP TABLE Command Removes a table from
the database specified with the data source and deletes
it from disk. INDEX Command Creates an index
file to display and access table records in a logical
order. INSERT - SQL Command Appends a record to
the end of a table that contains the specified field
values. SELECT - SQL Command Retrieves data from
one or more tables. The Visual FoxPro OLE DB
Provider supports the native Visual FoxPro language
syntax for this command.
SET ANSI Command
Determines how comparisons between strings of
different lengths are made with the = operator in Visual
FoxPro SQL commands. SET BLOCKSIZE Command
Specifies how disk space is allocated for the
storage of memo fields. SET COLLATE Command
Specifies a collation sequence for character fields
in subsequent indexing and sorting operations. SET
DELETED Command Specifies whether records marked for
deletion are processed and whether they are available
for use in other commands. SET EXACT Command
Specifies the rules for comparing two strings of
different lengths. SET EXCLUSIVE Command
Specifies whether table files are opened for
exclusive or shared use on a network. SET NULL
Command Determines how null values are supported by
the ALTER TABLE - SQL, CREATE TABLE - SQL, and INSERT -
SQL commands. SET PATH Command Specifies a path
for file searches. Provider Remarks If you issue
SET PATH in a stored procedure, it will be ignored by
the following functions and commands: SELECT, INSERT,
UPDATE, DELETE, and CREATE TABLE
If you issue
SET PATH in a stored procedure and do not subsequently
set the path back to its original state, other
connections to the database will use the new path
(because SET PATH is not scoped to data
sessions).
If you want to create, select, or
update tables in a directory other than that specified
by the data source, specify the full path of the file
with your command.
SET REPROCESS Command
Specifies how many times or for how long to lock a
file or record after an unsuccessful locking attempt.
SET UNIQUE Command Specifies whether records
with duplicate index key values are maintained in an
index file. UPDATE - SQL Command Updates records
in a table with new values.
-------------- приведу еще синтаксис create table
( так как отличается от MS SQL DDL)
Creates a
table having the specified fields.
CREATE TABLE |
DBF TableName1 [NAME LongTableName] [FREE]
(FieldName1 FieldType [(nFieldWidth [, nPrecision])]
[NULL | NOT NULL] [CHECK lExpression1 [ERROR
cMessageText1]] [DEFAULT eExpression1] [PRIMARY KEY
| UNIQUE] [REFERENCES TableName2 [TAG TagName1]]
[NOCPTRANS] [, FieldName2 ...] [, PRIMARY KEY
eExpression2 TAG TagName2 |, UNIQUE eExpression3 TAG
TagName3] [, FOREIGN KEY eExpression4 TAG TagName4
[NODUP] REFERENCES TableName3 [TAG TagName5]] [,
CHECK lExpression2 [ERROR cMessageText2]])| FROM ARRAY
ArrayName Parameters TableName1 Specifies the
name of the table to create. The TABLE and DBF options
are identical. NAME LongTableName Specifies a
long name for the table. A long table name can be
specified only when a database is open because long
table names are stored in databases. Long names can
contain up to 128 characters and can be used in place of
short file names in the database.
FREE
Specifies that the table will not be added to an
open database. FREE isn't required if a database isn't
open. (FieldName1 FieldType [(nFieldWidth [,
nPrecision])] Specifies the field name, field type,
field width, and field precision (number of decimal
places), respectively. A single table can contain up
to 255 fields. If one or more fields allow null values,
the limit is reduced by one to 254 fields.
FieldType is a single letter indicating the
field's data type. Some field data types require that
you specify nFieldWidth or nPrecision, or both.
The following table lists the values for
FieldType and whether nFieldWidth and nPrecision are
required.
FieldType nFieldWidth nPrecision
Description C n – Character field of width n D –
– Date T – – DateTime N n d Numeric field of
width n with d decimal places F n d Floating numeric
field of width n with d decimal places I – – Integer
B – d Double Y – – Currency L – – Logical
M – – Memo G – – General
nFieldWidth and
nPrecision are ignored for D, T, I, Y, L, M, G, and P
types. nPrecision defaults to zero (no decimal places)
if nPrecision isn't included for the N or F types.
nPrecision defaults to the number of decimal places
specified by the SET DECIMAL setting if nPrecision isn't
included for the B type.
NULL Allows null
values in the field. If one or more fields can contain
null values, the maximum number of fields the table can
contain is reduced by one, from 255 to 254. NOT NULL
Prevents null values in the field. If you omit
NULL and NOT NULL, the current setting of SET NULL
determines if null values are allowed in the field.
However, if you omit NULL and NOT NULL and include the
PRIMARY KEY or UNIQUE clause, the current setting of SET
NULL is ignored and the field defaults to NOT NULL.
CHECK lExpression1 Specifies a validation
rule for the field. lExpression1 can be a user-defined
function. Note that when a blank record is appended, the
validation rule is checked. An error is generated if the
validation rule doesn't allow for a blank field value in
an appended record. ERROR cMessageText1
Specifies the error message Visual FoxPro displays
when the validation rule specified with CHECK generates
an error. The message is displayed only when data is
changed within a Browse window or Edit window.
DEFAULT eExpression1 Specifies a default value
for the field. The data type of eExpression1must be the
same as the field's data type. PRIMARY KEY
Creates a primary index for the field. The primary
index tag has the same name as the field. UNIQUE
Creates a candidate index for the field. The
candidate index tag has the same name as the field. For
more information about candidate indexes, see Setting a
Primary or Candidate Index. Note Candidate indexes
(created by including the UNIQUE option in CREATE TABLE
or ALTER TABLE – SQL) are not the same as indexes
created with the UNIQUE option in the INDEX command. An
index created with the UNIQUE option in the INDEX
command allows duplicate index keys; candidate indexes
do not allow duplicate index keys. See INDEX for
additional information on its UNIQUE option. Null
values and duplicate records are not permitted in a
field used for a primary or candidate index. However,
Visual FoxPro will not generate an error if you create a
primary or candidate index for a field that supports
null values. Visual FoxPro will generate an error if you
attempt to enter a null or duplicate value into a field
used for a primary or candidate index.
REFERENCES TableName2 [TAG TagName1]
Specifies the parent table to which a persistent
relationship is established. If you omit TAG TagName1,
the relationship is established using the primary index
key of the parent table. If the parent table does not
have a primary index, Visual FoxPro generates an error.
Include TAG TagName1 to establish a relation based
on an existing index tag for the parent table. Index tag
names can contain up to 10 characters.
The
parent table cannot be a free table.
NOCPTRANS
Prevents translation to a different code page for
character and memo fields. If the table is converted to
another code page, the fields for which NOCPTRANS has
been specified are not translated. NOCPTRANS can only be
specified for character and memo fields. This will
create what appears in the Table Designer as Character
(binary) and Memo (binary) data types. The following
example creates a table named MYTABLE containing two
character fields and two memo fields. The second
character field CHAR2 and the second memo field MEMO2
include NOCPTRANS to prevent translation.
CREATE
TABLE mytable (char1 C(10), char2 C(10)
NOCPTRANS,; memo1 M, memo2 M NOCPTRANS) PRIMARY
KEY eExpression2 TAG TagName2 Specifies a primary
index to create. eExpression2 specifies any field or
combination of fields in the table. TAG TagName2
specifies the name for the primary index tag that is
created. Index tag names can contain up to 10
characters. Because a table can have only one
primary index, you cannot include this clause if you
have already created a primary index for a field. Visual
FoxPro generates an error if you include more than one
PRIMARY KEY clause in CREATE TABLE.
UNIQUE
eExpression3 TAG TagName3 Creates a candidate index.
eExpression3 specifies any field or combination of
fields in the table. However, if you have created a
primary index with one of the PRIMARY KEY options, you
cannot include the field that was specified for the
primary index. TAG TagName3 specifies a tag name for the
candidate index tag that is created. Index tag names can
contain up to 10 characters. A table can have
multiple candidate indexes.
FOREIGN KEY
eExpression4 TAG TagName4 [NODUP] Creates a foreign
(non-primary) index, and establishes a relationship to a
parent table. eExpression4 specifies the foreign index
key expression and TagName4 specifies the name of the
foreign index key tag that is created. Index tag names
can contain up to 10 characters. Include NODUP to create
a candidate foreign index. You can create multiple
foreign indexes for the table, but the foreign index
expressions must specify different fields in the table.
REFERENCES TableName3 [TAG TagName5]
Specifies the parent table to which a persistent
relationship is established. Include TAG TagName5 to
establish a relation based on an index tag for the
parent table. Index tag names can contain up to 10
characters. If you omit TAG TagName5, the relationship
is established using the parent table's primary index
key by default. CHECK eExpression2 [ERROR
cMessageText2] Specifies the table validation rule.
ERROR cMessageText2 specifies the error message Visual
FoxPro displays when the table validation rule is
executed. The message is displayed only when data is
changed within a Browse window or Edit window. FROM
ARRAY ArrayName Specifies the name of an existing
array whose contents are the name, type, precision, and
scale for each field in the table. The contents of the
array can be defined with the AFIELDS( ) function.
Remarks The new table is opened in the lowest
available work area, and can be accessed by its alias.
The new table is opened exclusively, regardless of the
current setting of SET EXCLUSIVE.
If a database
is open and you don't include the FREE clause, the new
table is added to the database. You cannot create a new
table with the same name as a table in the
database.
If a database isn't open when you
create the new table, including the NAME, CHECK,
DEFAULT, FOREIGN KEY, PRIMARY KEY, or REFERENCES clauses
generates an error.
Note that the CREATE TABLE
syntax uses commas to separate certain CREATE TABLE
options. Also, the NULL, NOT NULL, CHECK, DEFAULT,
PRIMARY KEY and UNIQUE clause must be placed within the
parentheses containing the column definitions.
| |
|
|
Автор:
Glory Прислал:
|
|
Вопрос порционной(постраничной) выборки тесно связан
с вопросом нумерации строк в запросе (см. FAQ.Возвращение порядковых номеров строк в
результирующем наборе). Другими словами, для того,
чтобы выбрать N-ую порцию из результатов запроса, нужно
сначала пронумеровать результаты этого
запроса.
Отсюда и похожие методы
решения
Вариант 1. «Классический».
SELECT TOP 100 * FROM MyTable
WHERE id NOT IN (SELECT TOP 100 id FROM MyTable ORDER BY id) ORDER BY id | Главный
недостаток этого метода в в том, что т.к. TOP n записей
выбираются уже из конечного результата запроса, то
проверка условия WHERE будет выполняться для каждой
строки главного запроса. При этом, время выполнения этой
проверки будет расти вместе с номером выбираемой
порции(страницы). Если, например, таблица содержит 100
записей и необходимо выбирать данные порциями по 10
записей, то для 2-ой порции нужно будет будет
проверять подзапрос из 10 записей для 3-ей порции
нужно будет будет проверять подзапрос из 20
записей для 4-ой порции нужно будет будет проверять
подзапрос из 30 записей и т.д.
Недостатком также
является невозможность задать значение для TOP с помощью
переменной. Что ведет к необходимости использования
динамического запроса.
Достоинство метода в его
универсальности, академичности. Он не требует специфики
T-SQL, этот метод можно применить практически на любом
SQL-сервере.
Вариант 2 «Эффективный,
специфический для T-SQL».
Как и в случае
нумерации строк данный метод основан на использовании
временной таблицы. Для удобства оформим наш запрос как
хранимую процедуру, возвращающую n-ую порцию(страницу),
содержащую m записей
CREATE PROCEDURE dbo.get_this_page (@rec_per_page int, @page_num int) AS
SELECT identity(int, 1,1) AS RowNum, MyId AS OrigId INTO #tmp FROM mytable
SELECT b.* FROM #tmp AS a
INNER JOIN mytable AS b on a.OrigId = b.MyId
WHERE a.RowNum BETWEEN (@rec_per_page * @page_num + 1)
AND (@rec_per_page * (@page_num + 1))
DROP TABLE #tmp | Примечания. -
предложенный вариант процедуры будет блокировать
некоторые системные таблицы базу tempdb на все время
выполнения 1-го запроса. Если время блокировки
становиться неприемлимым, то необходимо отделить
создание временной таблицы от заполнеения ее данными вот
запрос таким образом CREATE TABLE #temp(RowNum int
identity, OrigId int) INSERT INTO #temp(OrigId)
SELECT MyId FROM mytable
- Если, поле MyId было
создано признаком «IDENTITY», то это поле в запросе
необходимо «завернуть» в функцию «CONVERT», иначе будет
сообщение об ошибке.
Вариант 3 (от Cat2).
"Постраничная выборка, использующая временные
таблицы". Будет корректно работать только если на
полях, по которым производится выборка построены
индексы
/*
Создаем тестовую таблицу. По полям i1 и d строим уникальные индексы
*/
set nocount on
create table list (
id int identity(1,1) primary key,
i1 int,
i2 int,
d datetime
)
CREATE INDEX ind_i ON list(i1)
CREATE INDEX ind_d ON list(d)
go
set nocount on
declare @i int
declare @m int
set @i=1
while @i<=30
begin
insert into list (i1) values (null)
set @i=@i+1
end
/*
Заполняем тестовую таблицу. В поля i1 и i2 заносим числа от 1 до 30, а в d даты с 1 по 30 января
2000 года
*/
select id into #t from list
while exists (select * from list where i1 is Null)
begin
set @i=rand()*30+1
set @m=(select max(id) from #t)
update list
set i1=@m,
i2=@m,
d=convert(datetime,'20020101',112)+@m-1
where id=@i and i1 is null
if @@rowcount>0
delete from #t where id = @m
end
drop table #t
go
--Выбираем "строки" с 11 по 20
/*
Даже если выборка идет по первичному ключу его лучше указать.
В противном случае возможны всякие неожиданности
*/
SELECT identity(int,1,1) AS RowNum, convert(int,id) as id
into #t FROM list with (index(0))
select t.RowNum as [По id],t.id,l.i1,l.i2,l.d from #t t
join list l on l.id=t.id
where RowNum between 11 and 20
order by RowNum
drop table #t
go
/*
Выборка по полю, по которому нет индекса
Этот код не будет работать так, как надо, несмотря на order by i2
Подробднее об этом написано здесь - http://support.microsoft.com/default.aspx?scid=kb;en-us;273586
*/
SELECT identity(int,1,1) AS RowNum, i2, convert(int,id) as id
into #t FROM list
order by i2
select t.RowNum as [По i2],l.id,l.i1,t.i2,l.d
from #t t
join list l on l.id=t.id
where RowNum between 11 and 20
order by RowNum
drop table #t
go
/*
Выборка по полю, по которому индекс построен
Этот код делает то, что надо
*/
SELECT identity(int,1,1) AS RowNum, i1, convert(int,id) as id i
nto #t FROM list with (index(ind_i))
select t.RowNum as [По i1],t.id,t.i1,l.i2,l.d
from #t t
join list l on l.id=t.id
where RowNum between 11 and 20
order by RowNum
drop table #t
go
/*
Выборка по дате в порядке убывания
*/
SELECT identity(int,1,1) AS RowNum, d, convert(int,id) as id
into #t FROM list with (index(ind_d))
order by d desc
select t.RowNum as [По d],t.id,l.i1,l.i2,t.d
from #t t
join list l on l.id=t.id
where RowNum between 11 and 20
drop table #t
go
drop table list | Вариант
4 (от Глеба Уфимцева). Использование
курсора.
Предлагаемый метод не претендует на
звание самого лучшего. Он, может быть, и не лучше, но уж
точно не хуже других. Поиск решения основывался на
следующих пожеланиях:
1. Обращение к новой
странице выборки не должно приводить к перезапросу всей
выборки. 2. Запрос страницы должен возвращать обычный
привычный рекордсет. 3. Инициация не должна быть
чрезмерно долгой, чтобы запрос к таблице в 1000000 строк
не утомил ожиданием. 4. В любой момент должно быть
разрешено выбрать любую страницу из выборки, в том числе
и ранее выбранную. 5. Страница должна содержать любое
задаваемое кол-во строк. 6. Страница должна
начинаться с любой задаваемой строки по
номеру.
Решение, удовлетворяющее всем этим
пожеланиям стразу, было найдено. Вот оно:
1.
Имеем запрос, который мы хотим выбирать
постранично
мы
его не запускаем, а переходим к шагу 2.
2.
Инициализируем таким образом:
declare @handle int,@rows int
exec sp_cursoropen @handle OUT, 'select * from BigTable',1, 1, @rows OUT
select @handle, @rows | При
этом получаем пустой рекордсет, содержащий
метаданные-описания колонок, которые можно использовать
для получения названия полей (и типов). После
NextRecordset также получаем хендл получившегося курсора
и кол-во строк во всей выборке. Хендл нам понадобиться
для подстановки в следующие вызовы, и его надо сохранить
на клиенте в глобальную переменную, а кол-во строк может
быть использовано для определения кол-ва
страниц.
3. Получаем нужную страницу из
выборки:
exec sp_cursorfetch @handle,16,@rowid,@rowcount | Где
в @handle подставляем сохраненное значение хендла, в
@rowid подставляется номер строки, с которой начинается
интересующая нас страница, а в @rowcount подставляется
кол-во строк на данной странице.
Шаг 3 повторяем
столько сколько нужно раз.
4. Освобождаем ресурсы
после того, как страницы уже больше не
понадобятся
exec sp_cursorclose @handle | | | |
Отзывы
и оценки |
|
Timon |
20 сентября 2005, 06:14 |
Оценка: 5 |
Вариант с курсором вне конкуренции |
|
chenosov |
24 августа 2005, 11:04 |
Оценка: 5 |
Прекрасный способ 4. Жаль
недокументированы эти процедуры. И зачем нужно MS это
прятать? |
|
Hubbitus |
10 августа 2005, 15:28 |
Оценка: 5 |
4 вариант понравился больше всего, видится
мне он самым быстрым и универсальным, реализовал его в
качестве процедуры...
Но может кто-то подскажет,
как можно "убрать" пустой рекордсет от вызова
sp_cursoropen?? Буду очень благодарен. |
|
meclect |
26 июля 2005, 19:34 |
Оценка: N/A |
Забыто по крайней мере еще два варианта
:) 1. Классический модифицированный SELECT TOP 100
* FROM tt WHERE id in (SELECT TOP 100 ID FROM tt WHERE
ID not in ( SELECT top 100000 id FROM tt)) ORDER BY
id asc Добавляется еще один вложенный селект,
проверено, работает в два раза быстрее
классического
2. Извращенный SELECT * FROM tt
INNER join (SELECT TOP 10 id FROM (select top
100000 id from tt ORDER BY id ASC) A ORDER BY id
DESC) B ON B.id = tt.id ORDER BY tt.id ASC Смысл в
том, что выбираем первый топ, переворачиваем, выбираем
что надо и опять переворачиваем. Вместо INNER JOIN можно
использовать IN, но медленнее |
|
sp |
08 июля 2005, 16:59 |
Оценка: 4 |
A почему до сих пор никто не рассматривал
такой вариант ?
CREATE PROCEDURE
dbo.GetList @PageSize int, @PageNo
int, @PageCount int out AS SET NOCOUNT
ON
DECLARE @id bigint DECLARE @recordcount
int DECLARE @FirstRec int
IF (@PageNo = 0) SET
@PageNo=1 SELECT @recordcount=count(ID) FROM
Table SELECT @FirstRec = (@PageNo-1) * @PageSize +
1
if (@FirstRec<=@recordcount) begin SET
ROWCOUNT @FirstRec select @id = ID FROM Table ORDER
BY ID DESC
/* Get Table list */ SET ROWCOUNT
@PageSize SELECT <поля> FROM
Table WHERE <условие> ORDER BY ID
DESC end select @PageCount = CEILING(@recordcount
/ cast(@PageSize as real))
RETURN
@@ERROR GO |
|
Sergey |
28 марта 2005, 10:48 |
Оценка: 5 |
|
|
Автор:
fima Прислал: Glory
|
|
Q. Как организовать запрос к БД, чтобы он
возвращал не только данные, но и порядковые номера строк
в результирующем наборе ?
A.
Способов
решить эту задачу несколько. use pubs
set nocount on
if exists (select * from sysobjects where type = 'U' and name = 'test')
begin
drop table test
end
/* создание таблицы для примера */
create table test
(
id_test int identity not null,
string char (7),
constraint pk_test primary key (id_test)
)
/* установка флага для занесения с определенными ид. */
set identity_insert test on
/* занесение тестовых значений с произвольными ид. */
insert into test (id_test, string)
values (1,'string1')
insert into test (id_test, string)
values (4,'string2')
insert into test (id_test, string)
values (12,'string3')
insert into test (id_test, string)
values (17,'string4')
insert into test (id_test, string)
values (29,'string5')
insert into test (id_test, string)
values (31,'string6')
insert into test (id_test, string)
values (42,'string7')
insert into test (id_test, string)
values (45,'string8')
insert into test (id_test, string)
values (61,'string9')
/* отмена установки флага для занесения с определенными ид. */
set identity_insert test off
go
/* способ №1, создание проекции. */
if exists (select * from sysobjects where type = 'V' and name = 'ranked_table')
begin
drop view ranked_table
end
go
create view ranked_table (rank, id_test, sting)
as
select (
select count(*)
from test as test_2
where test_2.id_test <= test_1.id_test
) as rank,
test_1.id_test,
test_1.string
from test as test_1
go
select *
from ranked_table
order by rank
go
/* способ №2 стандартный SQL */
select count (test_2.id_test) as rank, test_1.id_test, test_1.string
from test as test_1 inner join test as test_2 on
test_1.id_test >= test_2.id_test
group by test_1.id_test, test_1.string
order by rank
go
/* способ №3 стандартный SQL */
select test_3.rank, test_3.id_test, test_3.string
from (select test_1.id_test,
test_1.string,
(select count(*)
from test as test_2
where test_2.id_test <= test_1.id_test
) as rank
from test as test_1) as test_3
order by rank
go
/* способ №4, временная таблица с полем identity */
create table #rank_table
(
rank int identity not null,
id_test int null,
string char (7),
constraint pk_test primary key (rank)
)
go
insert into #rank_table (id_test, string)
select id_test, string
from test
order by id_test
select * from #rank_table
go
/* способ №5, переменная типа table с полем identity */
declare @rank_table table
(
rank int identity not null,
id_test int null,
string char (7)
)
insert into @rank_table (id_test, string)
select id_test, string
from test
order by id_test
select * from @rank_table
go
/* способ №6, курсор */
declare @rank int,
@id_test int,
@string char (7)
declare rank_cursor cursor
for select id_test, string
from test
order by id_test
open rank_cursor
fetch next from rank_cursor into @id_test, @string
set @rank = 1
while (@@fetch_status <> -1)
begin
select @rank, @id_test, @string
set @rank = @rank + 1
fetch next from rank_cursor into @id_test, @string
end
close rank_cursor
deallocate rank_cursor
/* результат всех примеров
rank id_test string
----------- ----------- -------
1 1 string1
2 4 string2
3 12 string3
4 17 string4
5 29 string5
6 31 string6
7 42 string7
8 45 string8
9 61 string9
*/ |
Соответственно
нужно выбрать подходящий Вам. Обратите внимание что
пример №3 входит состовляющей частью в пример №1.
Поэтому на примере №2 тоже можно построить проекцию. На
мой взгляд, проекция на примере №2 наиболее оптимальна,
вместе с примером №5. Выбор из №2 и №5 зависит от
количества данных и пр.
Пример №5 будет работать
только на SQL 2000. На SQL 2000 лучше использовать
переменые типа table вместо временных таблиц, если это
возможно.
Пример №6 на мой взгляд, наиболее
неудачный и я привел его для общей эрудиции. Вроде
привел всё что вспомнил. Если кто знает еще способы
пишите. | | |
Отзывы
и оценки |
|
ЕвгенийВ |
23 августа 2005, 13:24 |
Оценка: 4 |
На таблице из 2000000 строк, запрос с
курсорами работает быстрее и много запроса с
view/count. |
|
|
Автор:
Glory Прислал:
|
|
Если get_xml - это процедура, в которой запускается
XML запрос, то ее результаты можно вывести во внешний
файл таким образом
DECLARE @result int
DECLARE @OutputFileName varchar(150)
DECLARE @cmd varchar( 150)
Set @OutputFileName = 'c:\mysql.xml'
Set @cmd = 'BCP "EXEC get_xml" queryout "' + @OutputFileName + '" -C ACP -c -r -T'
EXEC @result = master..xp_cmdshell @cmd | (BCP.EXE
- это утилита, поставляемая с SQL
сервером.)
Примечание Eсли в
запросе выбираются данные с кирилическими символами, то
вместо аргументов '-C ACP -c' нужно использовать '-w
-C1251'.
Т.к. в результатах XML запроса
отсутсвует пара тэгов <root></root>, то для
того, чтобы полученный файл можно было просматривать в
браузере эти теги нужно добавить "вручную". Это можно
сделать следующими способами
1. С помощью
дополнительной таблицы/переменной типа таблица.
CREATE PROCEDURE dbo.get_xml
as
SET NOCOUNT ON
declare @t table(fake_id varchar(10) )
select fake_id, name from @t root full outer join
(select top 10 name, id from sysobjects) AS sysobjects ON 1=1 for xml auto, elements | В
этом примере вы получите XML
вида <root><Element1><Attribute1>Value<Attribute1/><Attribute2>Value"<Attribute2/>....<Element1/></root>
2.
С помощью for xml explicit
CREATE PROCEDURE dbo.get_xml
as
SET NOCOUNT ON
select 1 as Tag,
NULL as Parent,
NULL as [root!1],
NULL as [sysobjects!2!name],
NULL as [sysobjects!2!id]
UNION all
SELECT TOP 10 2,
1,
NULL,
name as [sysobjects!2!name],
id as [sysobjects!2!id]
from sysobjects
for xml explicit | В
этом примере вы получите XML
вида <root><Element1 Attribute1="Value"
Attribute2="Value"..../>....</root>
Кроме
того записать результаты XML запроса в файл можно с
помощью простого VB Scripta
On Error Resume Next
Public cnn, rst, strXML, strConn
Set cnn = CreateObject("ADODB.Connection")
Set rst = CreateObject("ADODB.Recordset")
strConn = "PROVIDER=MSDASQL;DRIVER={SQL Server};" & _
"SERVER=<yourserver>;" & _
"DATABASE=<yourdb>;" & _
"UID=<yourlogin>;" & _
"PWD=<yourpasswd>;" & _
"Network=DBMSSOCN;" & _
"Address=<ip-address,port-number>;" & _
"UseProcForPrepare=0;" & _
"AutoTranslate=No"
With cnn
.ConnectionString = strConn
.Open strConn
End With
If cnn.State = 1 Then
Set rst = cnn.Execute("SELECT * FROM [MYTABLE] FOR XML RAW")
If rst.State = 1 Then
strXML = ""
While Not rst.EOF
strXML = strXML & rst.Fields(0).Value
rst.MoveNext
Wend
rst.Close
End If
cnn.Close
End If
WScript.Echo strXML | | | |
Отзывы
и оценки |
|
R1244 |
11 августа 2005, 08:42 |
Оценка: 5 |
Хорошая вещь, но как сделать вывод во
внешний файл из триггера, там без оператора GO скрипт не
срабатывает. GO --Без него пустой файл создается и
все подвисает DECLARE @result int DECLARE
@OutputFileName varchar(150) DECLARE @cmd varchar(
150) Set @OutputFileName = 'c:\mysql.xml' Set @cmd
= 'BCP "EXEC get_xml" queryout "' + @OutputFileName + '"
-C ACP -c -r -T' EXEC @result = master..xp_cmdshell
@cmd |
|
|
Автор:
Glory Прислал: Glory
|
|
Каким образом на T-SQL организовать запись во
внешний файл.
1-ый варинт
DECLARE @result int
EXEC @result = master..xp_cmdshell 'osql -S MYSQLSERVER -E -Q "SELECT TOP 10 * FROM pubs.dbo.authors" -b -o c:\myoutput.txt', no_output
IF (@result = 0)
PRINT 'Success'
ELSE
PRINT 'Failure' | |
2-ой
варинт
DECLARE @FileName varchar(255),
@Text1 varchar(8000),
@FS int,
@OLEResult int,
@FileID int,
@hr int,
@source varchar(30),
@desc varchar (200)
EXECUTE @OLEResult = sp_OACreate 'Scripting.FileSystemObject', @FS OUTPUT
IF @OLEResult <> 0
BEGIN
PRINT 'Scripting.FileSystemObject'
GOTO Error_Handler
END
execute @OLEResult = sp_OAMethod @FS,'CreateTextFile',@FileID OUTPUT, 'c:\xxx.txt'
IF @OLEResult <> 0
BEGIN
PRINT 'CreateTextFile'
GOTO Error_Handler
END
set @Text1 = 'blah-blah-blah' + char(0)
execute @OLEResult = sp_OAMethod @FileID, 'WriteLine', NULL, @Text1
IF @OLEResult <> 0
BEGIN
PRINT 'WriteLine'
GOTO Error_Handler
END
Print @Text1
goto Done
Error_Handler:
PRINT '*** ERROR ***'
EXEC @hr = sp_OAGetErrorInfo null, @source OUT, @desc OUT
SELECT hr = CONVERT (binary(4), @hr), source = @source, description = @desc
Done:
EXECUTE @OLEResult = sp_OADestroy @FileID
EXECUTE @OLEResult = sp_OADestroy @FS | |
3-ий
и IMHO самый правильный вариант - DTS
Package
Примечание 2-ой варианте по
неизвестным мне причинам может не работать (кажется
только в MS SQL7 и/или на WInNT4). Возможно проблема
решается установкой последнего последней версией
WSH. Возможно нет.
| |
'
| | | | |