Показаны сообщения с ярлыком MS SQL Server. Показать все сообщения
Показаны сообщения с ярлыком MS SQL Server. Показать все сообщения

02 февраля 2026

Получение расширения файла в SQL-запросе

Фантазия авторов PostgreSQL поражает. Его разнообразные синтаксические конструкции почти всегда позволяют решить задачу несколькими способами. Например, рассмотрим получение расширения файла из его имени. В отличии от Oracle и MS SQL Server, для PostgreSQL я насчитал 4 варианта.
PostgreSQL - это швейцарский нож

20 августа 2025

MS SQL Server. Таблицы оптимизированные для памяти и пропажа места на диске

К нам в поддержку обратился клиент с ошибкой "MAT/PIT export/import encountered a failure for memory optimized table or natively compiled stored procedure with object ID 1873539326 in database ID 5. The error code was 0x80030070." в приложении работающем с MS SQL Server.

MAT/PIT export/import encountered a failure for memory optimized table or natively compiled stored procedure with object ID <ID> in database ID <ID>
Так как в тексте ошибки упомянута таблица оптимизированная для памяти, то моя первая мысль была, что на сервере недостаточно памяти или она глюканула. Поиск описания ошибки по коду 0x80030070 вывел на STG_E_MEDIUMFULL "There is insufficient disk space to complete operation", а потом на ERROR_DISK_FULL 112 (0x70) "There is not enough space on the disk". Действительно, как мне потом написали, у клиента "всё починилось добавлением места". Как связаны таблицы оптимизированные для памяти и недостаток места на диске для завершения операции?

06 июня 2024

Преобразование JSON-строки в тип дата/время используя SQL

Согласно стандарту ECMA-404. The JSON Data Interchange Syntax в JSON нет типа для хранения даты и времени. В каком виде они будут закодированы в каждом конкретном JSON-файле определяет его автор. Я встречал два варианта:

  1. Целое число содержащее UNIX-время (UNIX-time или POSIX-time), которое используется в UNIX и других POSIX-совместимых операционных системах, и определяет количество секунд, прошедших с полуночи (00:00:00 UTC) 1 января 1970 года.
  2. Строка. Формат строки зависит от фантазии автора файла, но обычно используется стандарт ISO 8601, который представляет дату и время в универсальном формате, легко читаемом как людьми, так и машинами.
Недавно я средствами СУБД парсил JSON в котором дата/временя хранилась в строке формата "YYYY-MM-DDThh:mm:ss.sssZ" (например, "1975-11-21T01:34:53.666Z"). Процедура парсинга мне нужна была для трех СУБД: PostgreSQL, MS SQL Server и Oracle.

29 февраля 2024

Уникальные индексы и NULL

Согласно стандарту ANSI SQL, NULL – это специальное значение или псевдозначение, которое используется для обозначения отсутствия в поле базы данных какого-либо значения. О таком поле можно сказать, что оно имеет неопределенное значение или то, что оно пустое. Стандарт регламентирует, что NULL не равен NULL даже для полей с одинаковым типом данных. Хотя при этом строки, содержащие в поле NULL, группируются вместе при использовании DISTINCT или GROUP BY. В определении уникального ограничения SQL-92 ни слова не говорит про NULL: "A unique constraint is satisfied if and only if no two rows in a table have the same non-null values in the unique columns". Вероятно, это означает, что оно должно рассматривать каждый NULL, как уникальное значение. Как это реализовано в различных СУБД?

20 ноября 2023

MS SQL Server. Работа с данными от имени другого пользователя

    Я уже писал об использовании EXECUTE AS в MS SQL Server. Тогда я использовал "WITH EXECUTE AS OWNER" для изменения контекста безопасности на владельца триггера и "EXECUTE AS CALLER" для изменения контекста безопасности в коде триггера на пользователя, вызвавшего модуль. Хочу поговорить о вызове "EXECUTE AS" с указанием имени пользователя.

23 октября 2023

MS SQL Server. Запись данных из триггера в другую базу данных

    Представим ситуацию, в которой на сервере MS SQL Server есть две базы данных. С первой базой через свою информационную систему работают пользователи. Во вторую базу для обмена с другой системой выгружаются "итоговые данные, подписанные руководством". Самый простой вариант – это создать триггер, который после подписи будет копировать данные из таблицы первой базы данных во вторую.

28 июля 2023

Новый способ получения OUTPUT-значения после DML операции MS SQL Server в FireDAC Delphi 12

    Так получилось, что с периодичностью в два года я пишу о получении в программе значения первичного ключа, который сгенерирован СУБД при добавлении в таблицу новой строки. В 2019-м я писал об этом для UniDAC, потом в 2021-м для FireDAC. Разработчики Delphi 12 предоставили повод написать об этом и в 2023-м. В FireDAC для MS SQL Server, по аналогии с PostgreSQL и Firebird/InterBase, добавлена возможность получения OUTPUT-значения после DML операции с помощью параметров.

12 июня 2023

Сколько строк обработал оператор DML?

    После выполнения оператора DML модифицирующего данные иногда бывает необходимо узнать, сколько строк им было обработано. Это может быть полезно, например для определения успешности выполнения оператора или для ведения журнала операций. Каждая СУБД реализует такую функциональную возможность по-своему.

04 апреля 2023

PostgreSQL. Корректировка следующего значения полей SMALLSERIAL, SERIAL и BIGSERIAL

    У PostgreSQL, как и у многих других СУБД, есть возможность создавать в таблицах автоинкрементные столбцы. Для этого предназначены типы данных SMALLSERIAL, SERIAL и BIGSERIAL. К сожалению, авторы PostgreSQL не сделали никаких ограничений на прямую запись в столбцы этих типов. С одной стороны – это удобно. В таблицу можно записать данные, у которых уже есть значения для этого столбца. Но с другой стороны – это большая проблема. Такие действия могут привести к дублированию или к ошибке, когда позже при обычной вставке новой записи в таблицу СУБД попытается заполнить поле автоматически. Для сравнения, можно привести как продуманно это реализовано в MS SQL Server. Что бы записать значение в столбец, помеченный как IDENTITY, нужно это разрешить специальной командой "SET IDENTITY_INSERT": вызываем "SET IDENTITY_INSERT имя_таблицы ON", вставляем нужные записи и вызываем "SET IDENTITY_INSERT имя_таблицы OFF". При этом СУБД сама скорректирует текущее значение счетчика на максимальное значение столбца. Для PostgreSQL эту корректировку надо произвести вручную.

29 марта 2023

MS SQL Server. Управление контекстом безопасности подключения к связанным серверам

    Механизм связанных серверов MS SQL Server позволяет реализовать распределенные базы данных, которые работают с данными в других базах данных. "Связаться" можно с любым источником данных, для которого существует возможность подключения к нему с использованием OLE DB. Есть два шага обеспечения безопасности при подключении к удаленной базе данных связанного сервера:
  1. сопоставить имена пользователей локального сервера MS SQL Server с именами пользователей удаленного сервера;
  2. указать, как связанный сервер должен обрабатывать подключение пользователей, имена которых не сопоставлены.
Оба эти шага из T-SQL выполняются с помощью процедуры sp_addlinkedsrvlogin. Она создает или обновляет сопоставление между учетными записями пользователей локального и удаленного серверов. Название процедуры намекает на шаг с сопоставлением имен пользователей, но определенные комбинации ее параметров позволяют управлять подключением всех не сопоставленных пользователей.

21 октября 2021

FireDAC vs UniDAC. Получение значения первичного ключа новой строки

    Два года тому назад я писал о получении в программе значения первичного ключа, который сгенерирован СУБД при добавлении новой строки в таблицу. Мои примеры вызова INSERT с модификатором RETURNING (или OUTPUT в случае MS SQL Server) были с использованием библиотеки для доступа к базам данных UniDAC. Давайте посмотрим, как реализована эта возможность в библиотеке FireDAC, которая уже много лет входит в поставку Delphi и C++Builder.

22 сентября 2021

MS SQL Server. Получение из файловой системы списка папок и файлов

    Иногда работа с базами данных подкидывает не стандартные задачи. Например, недавно мне в скрипте MS SQL Server понадобилось получить из файловой системы список папок и файлов. Мои попытки сделать это через объект Scripting.FileSystemObject потерпели неудачу. Его метод GetFolder по имени папки возвращает объект Folder, у которого есть свойства SubFolders/Files содержащее списки вложенных папок и файлов. Но проблема в том, что эти списки – это коллекции, элементы которых в VBA можно перебрать в цикле "For Each File in Folder.Files", а из скрипта T-SQL к их элементам можно обратиться только по имени папки/файла (exec sp_OAMethod @objItems, N'Item("FileName.txt")', @objItem out). То есть для получения списка папок и файлов объект Scripting.FileSystemObject не подходит. Поиск в интернете позволил мне сформулировать 4 различные способа решения этой задачи.

08 сентября 2021

Фиксим "There is insufficient system memory in resource pool 'internal' to run this query" при запуске MS SQL Server

    Вчера MS SQL Server преподнес мне сюрприз. При создании новой базы данных он выдал ошибку "There is insufficient system memory in resource pool 'internal' to run this query" и "умер" – сервис SQL Server не запускался, а в ERRORLOG сыпались ошибки:
Msg 701, Level 17, State 130, Server XYZ, Line 1
There is insufficient system memory in resource pool 'internal' to run this query.

15 апреля 2021

Сортировка по текстовому полю содержащему комбинацию букв и цифр

    Сортировка результата SQL запроса – это то, с чем программисты сталкиваются постоянно. Обычно СУБД при сортировке текстовых полей для сравнения строк использует лексикографический порядок. Согласно Википедии, он означает, что слово X предшествует слову Y (X < Y), если:
  • либо слово X является началом слова Y (например, "МАТЕМАТИК" < "МАТЕМАТИКА");
  • либо первые m символов этих слов совпадают, а m+1-й символ слова X меньше m+1-го символа слова Y (например, "АБАК" < "АБРАКАДАБРА", так как первые две буквы у этих слов совпадают, а третья буква у первого слова меньше, чем у второго).
Этот способ сортировки строк подходит для большинства случаев. Проблемы могут возникнуть если строки содержат в себе комбинацию букв и цифр, а пользователи захотят видеть сортировку в "естественном порядке". Естественный порядок сортировки – это упорядочение строк в лексикографическом порядке, за исключением того, что многозначные числа обрабатываются атомарно (то есть, как один символ).
 Лексикографический порядок   Естественный порядок 
a12 a2a
a22 a2b
a2a a3a2
a2b a3a2a
a30z a12
a3a2 a22
a3a2a a30z

18 февраля 2021

MS SQL Server. Преобразование из ASCII в HEX и обратно

    Попросили меня написать скрипт, который конвертировал данные из одной таблицы в другую. При этом одно из текстовых полей нужно "привести к верхнему регистру, взять символы в обратном порядке и преобразовать их из ASCII в HEX". Например, строка "XYZ" должна превратиться в "5A5958". Привести к верхнему регистру и взять символы в обратном порядке – это делают встроенные строковые функции UPPER и REVERSE. Но, преобразование из ASCII в HEX поставило меня в тупик. Гугл мне в помощь!

31 января 2021

Быстрое заполнение нового столбца таблицы

    Недавно мне дали скрипт для обновления структуры базы данных на MS SQL Server. В нем было полно блоков, которые добавляли в таблицу столбец, заполняли его в существующих строках одинаковым значением и делали NOT NULL:
alter table MyTable add FIELD1 int null
go
update MyTable set FIELD1 = 1
go
alter table MyTable alter column FIELD1 int not null
go
Что будет, если количество строк в таблице измеряется не сотнями или тысячами, а миллионами или десятками миллионов?

08 октября 2020

Табличные переменные в динамическом SQL

    Табличные переменные являются одной из интересных возможностей MS SQL Server. Эта удобная альтернатива временным таблицам, которую можно использовать для хранения небольших наборов данных в виде строк таблицы. Сегодня мне впервые потребовалось использовать их совместно с динамическим SQL.

12 августа 2020

INSERT в таблицу без указания значений полей

    Все знают, что в команде INSERT имена полей таблицы не являются обязательными параметрами. Сразу скажу, что я никогда не использую подобный INSERT только с указанием значений полей, и вам не рекомендую. А вы задумывались, что список значений полей может тоже не являться обязательной частью этой команды?

15 октября 2019

Получение в программе значения первичного ключа после INSERT

    Часто бывает, что после добавления строки в таблицу необходимо получить сгенерированное в СУБД значение первичного ключа. Например, это необходимо для последующей вставки detail-данных. Самый "оригинальный" способ, что я видел: стартуем транзакцию, вызываем INSERT, делаем запрос "select MAX(ID) from table", вставляем detail-данные, коммитим... Но мы пойдем другим путем – без дополнительных запросов к базе данных. Мы воспользуемся вызовом оператора INSERT с модификатором RETURNING. Вопрос только в том, как получить в программе значение из RETURNING?

13 февраля 2011

Сохранение в базу данных отчета FastReport в формате PDF

   Недавно пришлось писать DLL, одна из функций которой должна была:
1. сформировать отчет в FastReport;
2. экспортировать отчет в файл PDF-формата и сохранить его в базе данных;
3. возвратить идентификатор сохраненного в базе данных файла.
Предположим, что исходная структура таблицы для хранения файла в базе данных была такая (MS SQLServer 2000):
CREATE TABLE X.FILES (
  ID BIGINT IDENTITY NOT NULL,
  FILE_BODY IMAGE NULL,
  CONSTRAINT PK_FILES PRIMARY KEY (ID)),
где FILE_BODY – это поле, в которое сохраняется файл, а ID – идентификатор файла.
   Для начала я создал процедуру, которая будет вставлять файл в базу данных и возвращать идентификатор вставленного файла.
CREATE PROCEDURE X.InsertFile
 @FILE image,
 @ID bigint OUTPUT
AS
 INSERT INTO X.FILES(FILE_BODY) VALUES(@FILE)
 SET @ID = SCOPE_IDENTITY()
GO
   Потом сел писать код DLL. В Delphi вся работа с файлами реализована с помощью потоков, поэтому я немного удивился, когда оказалось, что метод Export у TfrxReport экспортирует отчет только в файл (честно сказать, я ожидал увидеть, что-то в стиле ExportToStream). Т.е. вместо прямой передачи отчета через поток в базу данных, необходимо было сначала сохранить отчет во временный файл, а потом этот файл загрузить в базу данных. Мне это не понравилось, т.к. файловые операции (сначала записи, потом чтения) должны были хоть немного, но тормозить работу. Но нужно было срочно отдать DLL, поэтому я не стал разбираться с экспортом и сделал через файл:

...
 db: TSDDatabase; // база данных SQLDirect
 spInsertFile: TSDStoredProc; // вызов X.InsertFile
 frxReport: TfrxReport;
 frxPDFExport: TfrxPDFExport;
...

Function GetDoc(const sDotName: String; ...): Integer;
begin
 Result := -1;
 // загружаем шаблон отчета
 If frxReport.LoadFromFile(sDotName)
  then begin
   // устанавливаем параметры отчета
   ...
   If frxReport.PrepareReport
    then try
     // получаем имя временного файла
     frxPDFExport.FileName := GetTempFileName;
     Try
      // экспортируем отчет во временный файл
      frxReport.Export(frxPDFExport);
      // загружаем PDF-файл в параметр процедуры
      // из временного файла
      spInsertFile.Params[1].LoadFromFile(frxPDFExport.FileName, ftBlob);
     Except
      on E: Exception do
       WriteErrorMessage(E.Message);
     End;
     // сохраняем PDF-файл в базу данных
     Try
      db.StartTransaction;
      spInsertFile.ExecProc;
      db.Commit;
      Result := spInsertFile.Params[2].AsInteger;
     Except
      on E: ESDEngineError do
       begin
        db.Rollback;
        WriteErrorMessage(E.Message);
       end;
     End;
    Finally
     // удаляем временный файл
     DeleteFile(frxPDFExport.FileName)
    End
    else WriteErrorMessage('Ошибка подготовки отчета')
  end
  else WriteErrorMessage('Файл шаблона не найден')
end;

   После передачи DLL другому программисту, мысли о лишних файловых операциях при сохранении отчета в базу через временный файл, не давала мне покоя. Вечером, не найдя решения в документации по FastReport, я, прежде чем смотреть исходный код экспорта, решил еще раз пройтись по методам и свойствам экспорта. Глаз сразу же зацепился за свойство Stream. Я создал для этого свойства поток и метод Export у TfrxReport выгрузил отчет не в файл, а в поток.

Function GetDoc(const sDotName: String; ...): Integer;
begin
 Result := -1;
 // загружаем шаблон отчета
 If frxReport.LoadFromFile(sDotName)
  then begin
   // устанавливаем параметры отчета
   ...
   If frxReport.PrepareReport
    then begin
     Try
      Try
       // создаём поток в памяти
       frxPDFExport.Stream := TMemoryStream.Create;
       // экспортируем отчет в поток
       frxReport.Export(frxPDFExport);
       // загружаем PDF-файл в параметр процедуры
       // из потока в памяти
       spInsertFile.Params[1].LoadFromStream(frxPDFExport.Stream, ftBlob);
      Except
       on E: Exception do
        WriteErrorMessage(E.Message);
      End;
     Finally
      frxPDFExport.Stream.Free;
     End;
     // сохраняем PDF-файл в базу данных
     Try
      db.StartTransaction;
      spInsertFile.ExecProc;
      db.Commit;
      Result := spInsertFile.Params[2].AsInteger;
     Except
      on E: ESDEngineError do
       begin
        db.Rollback;
        WriteErrorMessage(E.Message);
       end;
     End;
    end
    else WriteErrorMessage('Ошибка подготовки отчета')
  end
  else WriteErrorMessage('Файл шаблона не найден')
end;

   Часто решение задачи лежит на поверхности и нет нужды копаться в чужом коде. Нужно лишь быть внимательным и никогда не сдавайтесь – "ищите и обрящите".