SQL Server - запрос соответствия атрибутов - PullRequest
0 голосов
/ 12 декабря 2018

SQL Server Gurus ...

В настоящее время использую MS SQL Server 2016

Я знаю, что Джо Селко и все пуристы SQL ломаются от мысли об использовании битовых масок, но у меня есть пример использованияв котором мне нужно запросить все виджеты, которые содержат набор заданных атрибутов.

  • Каждый виджет может содержать несколько сотен атрибутов.
  • Атрибуты виджета либо присутствуют, либо нет(1 = присутствует, 0 = нет)

Один из способов, которые я решил сделать, это с помощью битовых масок - атрибуты, которые нужно найти (битовая маска), могут быть объединены с атрибутами каждого виджета для поискасовпадения в одной операции.Например, таблица виджетов может быть:

widets table:
widget_uid  Uniqueidentifier
attributes  BigInt

SELECT widget_uid
   FROM widgets
  WHERE ( attributes & bitmask ) = bitmask;

Проблема в том, что использование BigInt для атрибутов ограничивает количество атрибутов до 64 (виджет может иметь несколько сотен атрибутов), я мог бы сгруппировать атрибуты вкуски 64 бит, то есть:

widets table:
widget_uid  Uniqueidentifier
attributes0 BigInt   -- Attributes 0-63
attributes1 BigInt   -- Attributes 64-127
attributes2 BigInt   -- Attributes 128-191

SELECT widget_uid
   FROM widgets
  WHERE ( attributes0 & bitmask0 ) = bitmask0
    AND ( attributes1 & bitmask1 ) = bitmask1
    AND ( attributes2 & bitmask2 ) = bitmask2

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

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

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

Заранее спасибо.

Ответы [ 2 ]

0 голосов
/ 14 декабря 2018

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

Используйте стандартное решение, это стандартная ситуация.Существует стандартное отношение M: N между виджетами и атрибутами (конечно, оба должны быть таблицами).Вы добавите еще одну таблицу, которая будет назначать атрибуты для виджетов - вы можете назвать его WidgetAttributes.

В нем будет 3 столбца: Id, WidgetId, AttributeId

Затем вы можете просто получить, например, списокВиджеты с атрибутом:

select w.* 
    from Widgets w
    inner join WidgetAttributes wa on wa.WidgetId = w.Id
    inner join Attributes a on a.Id = wa.AttributeId
    where a.AttributeName='xxx'
0 голосов
/ 12 декабря 2018

У нас был похожий вариант использования со значительно большим набором данных.Это было для сайта электронной коммерции с продуктами и атрибутами.Наш случай был немного сложнее, чем здесь, где у нас было любое возможное количество атрибутов, а затем значения, назначенные этим атрибутам.например, цвет - красный / зеленый / синий, размер - S / M / L и т. д.

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

Я могу привести пример кода, если вы считаете, что это будет полезно.

Отредактировано, чтобы добавить пример: DROP TABLE IF EXISTS #Widgets DROP TABLE IF EXISTS #Attributes DROP TABLE IF EXISTS # WidgetAttributes

CREATE TABLE #Widgets (widget_UID UNIQUEIDENTIFIER PRIMARY KEY CLUSTERED, Name NVARCHAR(255))
CREATE TABLE #Attributes  (Attribute_UID UNIQUEIDENTIFIER PRIMARY KEY CLUSTERED, Name NVARCHAR(255))
CREATE TABLE #WidgetAttributes (widget_UID UNIQUEIDENTIFIER,Attribute_UID UNIQUEIDENTIFIER)

CREATE NONCLUSTERED INDEX ix_WidgetAttribute ON #WidgetAttributes (Attribute_UID) INCLUDE (widget_UID)

INSERT INTO #Widgets (widget_UID, Name) values
( '{c63bea73-2331-4698-82c9-f71845ab8601}', N'Widget 1' ), 
( '{a0865b8f-606b-4273-9207-39a8a26016c4}', N'Widget 2' ), 
( '{211fe27e-ab98-4b61-83a3-3d006d66db5a}', N'Widget 3' )

INSERT INTO #Attributes (Attribute_UID, Name)
VALUES
( '{99354dc0-d0b2-4919-a887-edf115eeb1bd}', N'Height' ), 
( '{136bbe4c-497d-472f-a905-670e4a7805d0}', N'Width' ), 
( '{f006f950-30d1-453e-8e09-4f7d140fa3cb}', N'Depth' ), 
( '{0d190639-677f-4b75-8d36-1bdac00de132}', N'Colour' )

-- Set links
-- Widget 1 All attributes
-- Widget 2 Height Width 
-- Widget 3 Colour

INSERT INTO #WidgetAttributes (widget_UID, Attribute_UID)
SELECT  '{c63bea73-2331-4698-82c9-f71845ab8601}',Attribute_UID FROM #Attributes
UNION ALL
SELECT TOP (2) '{a0865b8f-606b-4273-9207-39a8a26016c4}',Attribute_UID FROM #Attributes WHERE Name<> 'Colour'
UNION ALL
SELECT  '{211fe27e-ab98-4b61-83a3-3d006d66db5a}',Attribute_UID FROM #Attributes WHERE Name = 'Colour'

-- @SearchAttributes to hold list of attributes you are trying to find
DECLARE @SearchAttributes TABLE (Attribute_UID UNIQUEIDENTIFIER)
INSERT INTO @SearchAttributes
SELECT Attribute_UID FROM #Attributes WHERE Name<> 'Colour'

;WITH cte AS (
SELECT WA.widget_UID, COUNT(1) AttributesPresent FROM #WidgetAttributes WA
JOIN @SearchAttributes SA ON SA.Attribute_UID = WA.Attribute_UID
GROUP BY WA.widget_UID
)
SELECT cte.AttributesPresent
     , W.widget_UID
     , W.Name 
FROM cte 
JOIN #Widgets W ON W.widget_UID = cte.widget_UID
ORDER BY cte.AttributesPresent DESC

Дает вывод:

AttributesPresent widget_UID                           Name
----------------- ------------------------------------ ----------
3                 C63BEA73-2331-4698-82C9-F71845AB8601 Widget 1
2                 A0865B8F-606B-4273-9207-39A8A26016C4 Widget 2

Мы использовали подходподсчитать, сколько атрибутов присутствовало для каждого, чтобы у нас была возможность не только «точного соответствия», но и «самого близкого соответствия».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...