Объединение таблиц на основе свободно определенного свободного текста - PullRequest
0 голосов
/ 01 октября 2019

Я хотел бы присоединиться к таблицам на основе плохо соблюдаемых бизнес-правил

У меня есть данные о встречах в следующем формате.

  • Некоторые встречи связаны с Идентификатор клиента , а другие нет.
  • Все назначения будут иметь фамилию и имя в информации о назначении, например, совещание по проекту: Тейлор, Джеймс ИЛИ [* HE] Тейлор, Джеймс:
    т.е. Фамилия и Имя, разделенные запятой и безбез пробела любая из сторон будет в столбце назначения

Таблица назначений выглядит следующим образом

ID| AppointmentTime         | Appointment                           |Client ID
23| 2019-09-30 09:15:00.000 | Project meeting :Taylor, James        | NULL  
34| 2019-09-30 09:20:00.000 | Project meeting :Taylor, James        | NULL
35| 2019-09-30 09:25:00.000 | Project meeting :Taylor, James        | NULL
36| 2019-09-30 10:25:00.000 | Pre sales : Hayes, John               | 2
47| 2019-09-30 10:30:00.000 | Project meeting :Manning, Richard     |425
50| 2019-09-30 14:30:00.000 | Closure meeting :Kuruvita, Peter      | NULL

Таблица клиентов выглядит следующим образом

ID  | Last Name | First Name 
2   | Hayes     | John               
425 | Manning   |Richard
3   | Taylor    | James

Iхотите иметь возможность присоединиться к таблице клиентов с таблицей назначений на основе приведенных ниже правил

  1. по возможности использовать идентификатор клиента в таблицах назначений и клиентов
  2. извлекать фамилии и имена из назначениястолбец и объединение, основанные на тех же столбцах в таблице клиента
  3. , где 2. выше невозможно, поскольку в клиенте нет записей, по-прежнему отображаются фамилия и имя из столбца назначения (возможно, с другим столбцом для указаниянет совпадений)

Ожидаемые результаты выглядят следующим образом

 ClientID    |Client Firstname  |Client Lastname |Match found 
    NULL     |Peter             |Kuruvita        |NO 
    3        |James             |Taylor          |YES
    2        |John              |Hayes           |YES
    425      |Richard           |Manning         |YES

Не могли бы вы помочь соператор SQL или фрагменты для разных частей, чтобы я мог соединить их вместе?

Реализация в Microsoft SQL Server 2008 R2 (SP2)

Ответы [ 2 ]

1 голос
/ 01 октября 2019

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

Ну, я бы пошел по этому пути (в v2008, который устарел *)1004 * ...)

Ваши данные в сценарии макета :

DECLARE @mockupTable TABLE(ID INT,AppointmentTime DATETIME,Appointment VARCHAR(1000),ClientID INT);
INSERT INTO @mockupTable VALUES
 (23,'2019-09-30T09:15:00.000','Project meeting :Taylor, James    ', NULL)  
,(34,'2019-09-30T09:20:00.000','Project meeting :Taylor, James    ', NULL)
,(35,'2019-09-30T09:25:00.000','Project meeting :Taylor, James    ', NULL)
,(36,'2019-09-30T10:25:00.000','Pre sales : Hayes, John           ', 2)
,(47,'2019-09-30T10:30:00.000','Project meeting :Manning, Richard ',425)
,(50,'2019-09-30T14:30:00.000','Closure meeting :Kuruvita, Peter  ', NULL);

- это запрос

SELECT t.*
      ,LTRIM(RTRIM(CastedAndSplit.value('/x[2]/y[1]/text()[1]','nvarchar(100)'))) AS LastName
      ,LTRIM(RTRIM(CastedAndSplit.value('/x[2]/y[2]/text()[1]','nvarchar(100)'))) AS FirstName
FROM @mockupTable t
CROSS APPLY(SELECT CAST('<x><y>' +  REPLACE(REPLACE((SELECT t.Appointment AS [*] FOR XML PATH('')),',','</y><y>'),':','</y></x><x><y>') + '</y></x>' AS XML)) A(CastedAndSplit);

Результат

+----+-------------------------+-----------------------------------+----------+-----------+----------+
| ID | AppointmentTime         | Appointment                       | ClientID | FirstName | LastName |
+----+-------------------------+-----------------------------------+----------+-----------+----------+
| 23 | 2019-09-30 09:15:00.000 | Project meeting :Taylor, James    | NULL     | Taylor    | James    |
+----+-------------------------+-----------------------------------+----------+-----------+----------+
| 34 | 2019-09-30 09:20:00.000 | Project meeting :Taylor, James    | NULL     | Taylor    | James    |
+----+-------------------------+-----------------------------------+----------+-----------+----------+
| 35 | 2019-09-30 09:25:00.000 | Project meeting :Taylor, James    | NULL     | Taylor    | James    |
+----+-------------------------+-----------------------------------+----------+-----------+----------+
| 36 | 2019-09-30 10:25:00.000 | Pre sales : Hayes, John           | 2        | Hayes     | John     |
+----+-------------------------+-----------------------------------+----------+-----------+----------+
| 47 | 2019-09-30 10:30:00.000 | Project meeting :Manning, Richard | 425      | Manning   | Richard  |
+----+-------------------------+-----------------------------------+----------+-----------+----------+
| 50 | 2019-09-30 14:30:00.000 | Closure meeting :Kuruvita, Peter  | NULL     | Kuruvita  | Peter    |
+----+-------------------------+-----------------------------------+----------+-----------+----------+

Идея вкратце:

CROSS APPLY будет использовать трюк с XML, чтобы сначала разбить вашу строку на :, а затем на ,. Полученный XML будет выглядеть следующим образом:

<x>
  <y>Project meeting </y>
</x>
<x>
  <y>Manning</y>
  <y> Richard </y>
</x>

Мы можем использовать XPath /x[2], чтобы получить часть после : и /y[1] или /y[2], чтобы получить первый иливторой фрагмент имени. Остальное обрезается ...

Я оставляю за вами возможность сформулировать соединение с вашим клиентским столом, это должно быть легко ...

1 голос
/ 01 октября 2019

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

Исправление проблемной таблицыс запросом никогда не бывает так желательно, как исправить таблицу и затем выполнить простой запрос.

Если вы уже пробовали этот маршрут и не можете изменить таблицу, вот один из подходов, который использует временную таблицус постановкой дела для «постановки» данных. Это ужасно. Мой менеджер был бы в ужасе, если бы я когда-либо развернул такой запрос на реальных данных на работе. Но это даст вам полезный вывод. Моя стратегия заключалась в том, чтобы вырвать значения подстроки. Имейте в виду, что эта стратегия потерпит неудачу, и запрос будет сорван, если ваши данные введены непоследовательно.

Пока мы обсуждаем эту тему, рекомендуется не включать пробелы в имена столбцов (плохо = [КлиентИмя]. Good = [client_name]). Это помогает избежать поломок.

/*I created two temp tables to show more clearly what is happening with the data.
 In the first temp table, we create a column called client_name that includes only
 the characters after the ':' in the Appointment column.
 SUBSTRING() extracts all characters after the colon.
 LTRIM() trims off any leading spaces, since some rows have them and others don't;
 for instance, ': Hayes' has a space but ':Kuruvita' does not.
 */

DROP TABLE IF EXISTS #apptstaging
SELECT 
    LTRIM(SUBSTRING(Appointment, CHARINDEX(':',Appointment)+1, LEN(Appointment))) AS client_name
    ,ID
    ,AppointmentTime
    ,[Client ID]
INTO #apptstaging
FROM [Appointment]

/* In this second query, I pulled out everything after the first comma, and assume 
that the comma has a space after it. This is the first name. Then, I pull out the
last name from all the characters before the first comma.
Again, creating multiple temp tables is somewhat redundant, but I'm doing it to make
it clear what I'm doing with the data. */

drop table if exists #namestaging
SELECT 
    SUBSTRING(client_name, CHARINDEX(',',client_name)+2, (LEN(client_name)-(CHARINDEX(',',client_name)))) as First_Name 
    ,SUBSTRING(client_name, 1, CHARINDEX(',',client_name)-1) AS Last_Name
    ,a.*
into #namestaging
from #apptstaging

/*The case statement returns 'Yes' if the name has a match in the Client table.
I then join the temp table to the Client table.*/

SELECT
     ns.[client ID]
    ,ns.First_Name
    ,ns.Last_Name
    ,CASE 
         WHEN c.ID IS NOT NULL THEN 'Yes'
         ELSE 'No'
         END AS 'Match Found'
FROM #namestaging ns
     LEFT JOIN [Client] c
        ON c.[last name] = ns.last_name
        AND c.[first name] = ns.first_name
...