CHARINDEX не возвращает правильный индекс в строке поиска

У меня есть следующие данные в одном элементе в таблице базы данных:

Port Jefferson Station, NY 11776-2598   
Northport, NY 11768                     
Garden City, NY 11530                   

Я пытаюсь разбить каждую строку на новую таблицу, содержащую поле города, поле штата и поле почтового индекса.

Я запускаю скрипт со следующим кодом, чтобы получить значение города:

Create function [dbo].[fnBuildCityState](@AddressCity varchar(40))
RETURNS @NameTable TABLE
(
    City VARCHAR(255),
    State VARCHAR(255),
    Zip VARCHAR(255)
) 
as
BEGIN

-- Index and word variables.
DECLARE @AccountNameEndIndex INT;
DECLARE @FirstWordEndIndex   INT;
DECLARE @SecondWordEndIndex  INT;
DECLARE @ThirdWordEndIndex   INT;
DECLARE @FirstWord       NVARCHAR(8);
DECLARE @SecondWord      NVARCHAR(50);
DECLARE @ThirdWord       NVARCHAR(50);

SET @AddressCity = RTRIM(ISNULL(@AddressCity,'No Address, City ZipCode'))
if (LEN(@AddressCity)<3)
BEGIN
    SET @AddressCity = 'No Address, City ZipCode'
END

-- Get the last index.
SET @AccountNameEndIndex = LEN(@AddressCity) - 1;
-- Get the first word.
SET @FirstWordEndIndex = CHARINDEX(',', @AddressCity, 0);
SET @FirstWord         = SUBSTRING(@AddressCity, 0, @FirstWordEndIndex);



INSERT INTO @NameTable (City, State, Zip)
VALUES (@FirstWord, @SecondWord, @ThirdWord)

RETURN
END

Когда я запускаю код и смотрю на результирующую таблицу, я вижу этот вывод:

City                     State                Zip
Port Jef                 NULL                 NULL
Northpor                 NULL                 NULL
Garden C                 NULL                 NULL

Строка кода, которая должна возвращать индекс к местоположению запятой во входных данных,

SET @FirstWordEndIndex = CHARINDEX(',', @AddressCity, 0);

Кажется, он всегда возвращает значение 8 независимо от того, где в строке стоит запятая.

Есть идеи, почему?

вы объявили переменную только размером 8

Squirrel 28.12.2022 23:02
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
1
1
56
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Конкретный ответ на ваш вопрос заключается в том, что вы определили @FirstWord как NVARCHAR(8), поэтому он будет содержать только до 8 символов.

Но главный вопрос в том, почему вы пытаетесь это сделать RBAR? SQL основан на наборах по какой-то причине... вы можете создать запрос, чтобы сделать все, что вам нужно, одним нажатием, например.

SELECT b.City, C.[State], c.ZipCode
FROM (
    VALUES
    ('Port Jefferson Station, NY 11776-2598'),
    ('Northport, NY 11768'),
    ('Garden City, NY 11530')
) x ([Address])
CROSS APPLY (
    VALUES
    (CHARINDEX(',', x.[Address], 0))
) a (FirstComma)
CROSS APPLY (
    VALUES
    (SUBSTRING(x.[Address],1,a.FirstComma-1)
    , SUBSTRING(x.[Address],a.FirstComma+2,LEN(x.[Address])))
) b (City,SecondString)
CROSS APPLY (
    VALUES
    (SUBSTRING(b.SecondString,1,2)
    , SUBSTRING(b.SecondString,4,LEN(b.SecondString)))
) c ([State],ZipCode);

Возвращает:

Город Состояние Почтовый индекс Станция Порт-Джефферсон Нью-Йорк 11776-2598 Нортпорт Нью-Йорк 11768 Город-сад Нью-Йорк 11530

Это упрощенно и не охватывает крайние случаи, такие как отсутствие адреса или состояние с длиной, отличной от 2 символов. Но это все вещи, которые вы можете легко разобрать.

Смысл использования CROSS APPLY заключается в том, чтобы избежать повторения одних и тех же вычислений в нескольких местах, что потребовалось бы для запроса без CROSS APPLY.

Я ценю ваш вопрос/объяснение. Это всего лишь небольшая часть более крупного скрипта, который будет поступать от стороннего поставщика и нуждается в обмене сообщениями перед вставкой в ​​наши таблицы. Что касается вашего первого пункта, ограничение в 8 символов было ошибкой, поскольку код был скопирован из другого источника, и мне не удалось это изменить. Спасибо!

Jonathan Small 29.12.2022 15:05

Пожалуйста, попробуйте следующее решение.

Он основан на токенизации, а не на разборе строк.

SQL

-- DDL and sample data population, start
DECLARE @tbl TABLE (ID INT IDENTITY PRIMARY KEY, tokens VARCHAR(100));
INSERT @tbl (tokens) VALUES
('Port Jefferson Station, NY 11776-2598'),
('Northport, NY 11768'),   
('Garden City, NY 11530');
-- DDL and sample data population, end

DECLARE @separator CHAR(1) = ',';

SELECT t.*
    , city = c.value('(/root/r[1]/text())[1]', 'VARCHAR(50)')
    , state = LEFT(TRIM(c.value('(/root/r[2]/text())[1]', 'VARCHAR(50)')), 2)
    , zip = SUBSTRING(TRIM(c.value('(/root/r[2]/text())[1]', 'VARCHAR(50)')), 3, 50)
FROM @tbl AS t
CROSS APPLY (SELECT TRY_CAST('<root><r><![CDATA[' + 
      REPLACE(tokens, @separator, ']]></r><r><![CDATA[') + 
      ']]></r></root>' AS XML)) AS t1(c);

Выход

ИДЕНТИФИКАТОР жетоны город состояние молния 1 Станция Порт-Джефферсон, Нью-Йорк 11776-2598 Станция Порт-Джефферсон Нью-Йорк 11776-2598 2 Нортпорт, Нью-Йорк 11768 Нортпорт Нью-Йорк 11768 3 Гарден-Сити, Нью-Йорк, 11530 Город-сад Нью-Йорк 11530

Я склоняюсь к JSON при разборе строк

Пример

Select City  = JSON_VALUE(JS1,'$[0]')
      ,State = JSON_VALUE(JS2,'$[0]')
      ,ZIP   = JSON_VALUE(JS2,'$[1]')
 From YourTable A
 Cross Apply (values ('["'+replace(Address,',','","')+'"]') ) B(JS1)
 Cross Apply (values ('["'+replace(ltrim(JSON_VALUE(JS1,'$[1]')),' ','","')+'"]') ) C(JS2)

Полученные результаты

City                    State   ZIP
Port Jefferson Station  NY      11776-2598
Northport               NY      11768
Garden City             NY      11530

Если вы хотели использовать в качестве TVF

Declare @AddressCity varchar(max) = 'Port Jefferson Station, NY 11776-2598'

Select City  = JSON_VALUE(JS1,'$[0]')
      ,State = JSON_VALUE(JS2,'$[0]')
      ,ZIP   = JSON_VALUE(JS2,'$[1]')
 From (values ('["'+replace(@AddressCity,',','","')+'"]') ) A(JS1)
 Cross Apply (values ('["'+replace(ltrim(JSON_VALUE(JS1,'$[1]')),' ','","')+'"]') ) B(JS2)

Другие вопросы по теме