2016-02-17 3 views
0

Я надеюсь, что кто-то может дать мне объяснение следующую проблему, связанную с преобразованием varchar в datetime.SQL - непоследовательная проблема преобразования даты и времени

Код ниже работы:

SELECT 
    mt.matter_code, 
    ud.uds_type, 
    ud.group_no, 
    ud.ud_field##2, 
    convert(datetime,ud.ud_field##2,103) hearingDate 
FROM dbo.matdb mt 
    INNER JOIN dbo.matdb_add_in ad 
      ON mt.mt_int_code = ad.mt_int_code 
      AND ad.add_in_code = 'OUTAA' 
    INNER JOIN dbo.uddetail ud 
      ON convert(varchar,ad.mt_add_in_int_code) = ud.owner_code 
      AND ud.parent_code = ad.add_in_code 
      AND ud.po_type_char = 'A' 
      AND ud.uds_type = 'LPR' 
      --AND ud.uds_type IN (SELECT s FROM dbo.split(',','LPR')) 
WHERE mt.mt_type = 'MATA' 
AND mt.matter_code = '118-1' 
ORDER BY ud.ud_field##2 ASC; 

Однако, если мы подставим

'AND ud.uds_type = 'LPR' 

ДЛЯ

'AND ud.uds_type IN (SELECT s FROM dbo.split(',','LPR'))' 

Я получаю следующее сообщение об ошибке преобразования: «не удалось преобразования при преобразовании datetime из символьной строки. '

Обе строки кода возвращают ТОЧНЫЕ одинаковые результаты, когда не используются преобразования. Однако, как только будет введено преобразование при использовании dbo.split(), я получаю сообщение об ошибке, упомянутое выше.

Я пропустил какую-то ссылку или это ошибка?

EDIT: функция dbo.split

USE [Vfile_Dev] 
GO 

/****** Object: UserDefinedFunction [dbo].[Split] Script Date: 02/18/2016 13:28:12 ******/ 
SET ANSI_NULLS ON 
GO 

SET QUOTED_IDENTIFIER ON 
GO 

CREATE FUNCTION [dbo].[Split] (@sep char(1), @s varchar(512)) 
RETURNS table 
AS 
RETURN (
    WITH Pieces(pn, start, stop) AS (
     SELECT 1, 1, CHARINDEX(@sep, @s) 
     UNION ALL 
     SELECT pn + 1, stop + 1, CHARINDEX(@sep, @s, stop + 1) 
     FROM Pieces 
     WHERE stop > 0 
    ) 
    SELECT pn, 
     SUBSTRING(@s, start, CASE WHEN stop > 0 THEN stop-start ELSE 512 END) AS s 
    FROM Pieces 
) 
GO 

EDIT: Если кому-то интересно, я обеспечил решение, ниже которого стало возможным благодаря Ивану. Дополнительные проверки были сделаны на ud.ud_field ## 2, чтобы убедиться, что не было пробелов, и результаты dbo.split теперь вставляются в переменную таблицы.

INSERT INTO @screenTable 
SELECT s FROM dbo.split(',','LPR,COU'); 

SELECT 
    mt.matter_code, 
    ud.uds_type, 
    ud.group_no, 
    isnull(nullif(ud.ud_field##2,''),'01/01/1990') as hdNorm, 
    convert(datetime, isnull(nullif(ud.ud_field##2,''),'1990-01-01 00:00:00.000')) AS hdConverted 
FROM dbo.matdb mt 
    INNER JOIN dbo.matdb_add_in ad 
      ON mt.mt_int_code = ad.mt_int_code 
      AND ad.add_in_code = 'OUTAA' 
    INNER JOIN dbo.uddetail ud 
      ON convert(varchar, ad.mt_add_in_int_code) = ud.owner_code 
      AND ud.parent_code = ad.add_in_code 
      AND ud.uds_type IN (select * from @screenTable) 
WHERE mt.mt_type = 'MATA' 
AND mt.matter_code = '118-1' 
ORDER BY group_no ASC; 
+1

Как насчет совместного использования функции разделения? –

+0

Просьба привести минимальный пример, демонстрирующий вашу проблему. http://stackoverflow.com/help/how-to-ask –

+0

У вас есть недопустимые данные в таблице, которые не соответствуют вашим критериям поиска? В зависимости от случая, которое вы можете читать +, конвертируя также те строки, которые будут удалены позже, но затем произойдет ошибка. –

ответ

1

Я думаю, что ud.ud_field##2 содержит больше, чем просто datetime данных. ud.uds_type и одна из причин, по которой вам нужен convert(datetime,ud.ud_field##2,103). Когда вы используете предложение WHERE напрямую, предикат применяется до вызывается функция CONVERT. Это означает, что SQL Server преобразует только данные, которые можно преобразовать. Когда вы используете функцию split, SQL Server больше не может этого сделать.

Если ud.ud_field##2 используется для хранения нескольких типов данных, у вас возникает проблема с дизайном. Существует несколько различных решений (например, создайте несколько таблиц uddetail - по одному для каждого типа данных, добавьте столбцы в uddetail - по одному для каждого типа данных и т. Д.).

Вот мой пример:

CREATE TABLE #DifferentData1 
    (
    [DataType]   VARCHAR(20),-- This is our code for the type of data we have in Sometimes_datetime 
    [Sometimes_datetime] VARCHAR(20)-- This is datetime or something else 
) 

CREATE TABLE #DifferentData2 
    (
    [DataType]  VARCHAR(20),-- This is our code for the type of data we have 
    [Always_datetime] DATETIME,-- This is ALWAYS datetime 
    [Other_data]  VARCHAR(50) 
) 

INSERT INTO #DifferentData1 
      ([DataType], 
      [Sometimes_datetime]) 
VALUES  ('LPR',CONVERT(VARCHAR, GETDATE(), 103)), 
      ('Almost LPR',CONVERT(VARCHAR, GETDATE(), 120)), 
      ('Not LPR','Some other data type') 

INSERT INTO #DifferentData2 
      ([DataType], 
      [Always_datetime], 
      [Other_data]) 
VALUES  ('LPR',CONVERT(DATETIME, GETDATE(), 103),NULL), 
      ('Almost LPR',NULL,CONVERT(VARCHAR, GETDATE(), 120)), 
      ('Not LPR',NULL,'Some other data type') 

-- Let's see what we have 
SELECT [DataType], 
     [Sometimes_datetime] 
FROM #DifferentData1 

SELECT [DataType], 
     [Always_datetime], 
     [Other_data] 
FROM #DifferentData2 

Теперь мы можем фильтровать с помощью простого ИНЕКЯ и все отлично подходит для любой таблицы:

SELECT CONVERT(DATETIME, [Sometimes_datetime], 103) 
FROM #DifferentData1 
WHERE [DataType] = 'LPR' 

SELECT CONVERT(DATETIME, [Always_datetime], 103) 
FROM #DifferentData2 
WHERE [DataType] = 'LPR' 

Это работает для #DifferentData1, поскольку предикат (ИНЕКЕ) применяется доCONVERT. Однако при использовании функции split SQL Sever пытается выполнить CONVERTпосле применяется предикат. Это приводит к сбою.#DifferentData2 все равно будет работать как задумано:

-- This fails 
SELECT CONVERT(DATETIME, [Sometimes_datetime], 103) 
FROM #DifferentData1 
WHERE [DataType] IN 
     (SELECT [SplitData] 
     FROM [dbo].[SplitString]('LPR', ',')) 
OPTION (MERGE JOIN) 

-- This works! 
SELECT CONVERT(DATETIME, [Always_datetime], 103) 
FROM #DifferentData2 
WHERE [DataType] IN 
     (SELECT [SplitData] 
     FROM [dbo].[SplitString]('LPR', ',')) 
OPTION (MERGE JOIN) 

Примечание: Я должен был заставить MERGE JOIN, поскольку предикат может применяться до CONVERT если LOOP JOIN используется. Если ваш набор данных невелик, может быть просто принудительно подключить LOOP JOIN, но это НЕ рекомендуется в качестве общего решения:

SELECT CONVERT(DATETIME, [Sometimes_datetime], 103) 
FROM #DifferentData1 
WHERE [DataType] IN 
     (SELECT [SplitData] 
     FROM [dbo].[SplitString]('LPR', ',')) 
OPTION (LOOP JOIN) 
+0

Для подтверждения - ud.ud_field ## 2 всегда будет содержать допустимый формат даты для преобразования в дату и время. dbo.split используется только для разделения запятой и сохранения ее в таблице. Я редактировал свой пост, чтобы включить функцию dbo.split. –

Смежные вопросы