Структура на данните, позволяваща търсене по ред

Бих искал да знам каква структура на данните/стратегия за съхранение трябва да използвам за този проблем.

Всеки запис на данни в базата данни се състои от списък с множество подредени елементи, като A-B-C-D, където A, B, C, D са различни елементи.

Да предположим, че имам 3 записа в база данни,

A-B-C-D

E-F-G

G-H-B-A

Когато потребителят въведе някои неподредени елементи, трябва да намеря съвпадащия подреден(и) запис(и) от базата данни. Например, ако потребител въведе A,B,G,H, искам да върна G-H-B-A от базата данни на потребителя.

Каква трябва да бъде моята стратегия за съхранение на данни?


person gilbertc    schedule 07.02.2010    source източник


Отговори (2)


Най-добре е да съхранявате подредените и неподредените елементи отделно, в противен случай ще трябва да търсите във всички пермутации на подредените елементи, което би отнело време.

Опитайте тази:

/* Create a table to track your items (A, B, C, etc.). It contains all possible elements */
CREATE TABLE [Items](
    [Value] [char](1) NOT NULL,
 CONSTRAINT [PK_Items] PRIMARY KEY CLUSTERED ([Value]))

/* Create a table to track their grouping and stated ordering */
CREATE TABLE [Groups](
    [ID] [int] NOT NULL,
    [Order] [text] NOT NULL,
 CONSTRAINT [PK_Groups] PRIMARY KEY CLUSTERED ([ID]))

/* Create a mapping table to associate them */
CREATE TABLE [ItemsToGroups](
    [Item] [char](1) NOT NULL,
    [Group] [int] NOT NULL
)

ALTER TABLE [ItemsToGroups]  WITH CHECK ADD CONSTRAINT [FK_ItemsToGroups_Groups] FOREIGN KEY([Group])
REFERENCES [Groups] ([ID])

ALTER TABLE [ItemsToGroups] CHECK CONSTRAINT [FK_ItemsToGroups_Groups]

ALTER TABLE [ItemsToGroups]  WITH CHECK ADD CONSTRAINT [FK_ItemsToGroups_Items] FOREIGN KEY([Item])
REFERENCES [Items] ([Value])

ALTER TABLE [ItemsToGroups] CHECK CONSTRAINT [FK_ItemsToGroups_Items]

/* Populate your tables. 
   Items should have eight rows: A, B, C,...H
   Groups should have three rows: 1:ABCD, 2:EFG, 3:GHBA
   Items to groups should have eleven rows: A:1, B:1,...A:3 */

/* You will want to pass in a table of values, so set up a table-valued parameter
   First, create a type to support your input list */
CREATE TYPE ItemList AS TABLE (e char(1) NOT NULL PRIMARY KEY)
DECLARE @Input ItemList
GO

/* Create a stored procedure for your query */
CREATE PROCEDURE SelectOrderedGroup @Input ItemList READONLY AS
    SELECT *
    FROM Groups
    WHERE Groups.ID NOT IN (
        SELECT [Group]
        FROM ItemsToGroups
        WHERE Item NOT IN (SELECT e FROM @Input)
    )
GO

/* Now when you want to query them: */
DECLARE @MyList ItemList
INSERT @MyList(e) VALUES('G'),('H'),('B'),('A')
EXEC SelectOrderedGroup @MyList

Горното ще върне 3:GHBA, както искате. Ако преминете през DCBA, ще получите обратно 1:ABCD, отново както търсите. Ако преминете в C, няма да получите обратно нищо, тъй като никоя група не се състои само от C.

Вероятно ще искате да използвате параметър със стойност на таблица за вашия вход , както е показано по-горе, но можете да конвертирате крайния SELECT в прост списък и да премахнете типа ItemList.

person Justin R.    schedule 09.02.2010

Разделете списъците на отделни елементи и работете на това ниво.

Някои таблици:

списъци

  • ID (PK)
  • последователност (записите "A-B-C-D" по-горе)
  • [каквото и да е друго]

елементи

  • ID (PK)
  • име (стойност, дума, каквото има смисъл)
  • [каквото и да е друго]

списък_елементи

  • list_ID
  • item_ID
  • [порядък int, ако "G-H-B-A" и "A-B-G-H" се считат за различни последователности]

(композитен PK list_ID, item_ID [, порядък] на този, основна връзка много:много)

Някои данни, за да е по-ясно какво представляват таблиците:

INSERT INTO items (ID, name) VALUES (1, 'A'), (2, 'B'), (3, 'G'), (4, 'H');
INSERT INTO lists (ID, sequence) VALUES (1, 'A-B-G-H');
INSERT INTO list_items (list_ID, item_ID) VALUES (1, 1), (1, 2), (1, 3), (1, 4);
INSERT INTO lists (ID, sequence) VALUES (2, 'B-A-G');
INSERT INTO list_items (list_ID, item_ID) VALUES (2, 2), (2, 1), (2, 3);

И накрая, за да намерите списъци, които съдържат всички елементи (A, B, G, H):

SELECT lists.sequence FROM lists
JOIN list_items ON lists.ID = list_items.list_ID
JOIN items AS i1 ON list_items.item_ID = i1.ID HAVING i1.name = 'A'
JOIN items AS i2 ON list_items.item_ID = i2.ID HAVING i2.name = 'B'
JOIN items AS i3 ON list_items.item_ID = i3.ID HAVING i3.name = 'G'
JOIN items AS i4 ON list_items.item_ID = i4.ID HAVING i4.name = 'H'

Това трябва да върне всички списъци като "A-B-G-H", "G-H-A-B", "H-A-T-B-A-G" и т.н., но не и "B-U-G-H-U-T" (без A) или "B-A-T-H" (без G) - всички условия трябва да бъдат изпълнени. Извършването на „всяко“ търсене може да е малко по-ангажиращо (да пиша това в главата си по време на обяд, но само RIGHT JOIN вероятно ще доведе до всякакви дублирания и бавност).

Той няма да картографира никакви геноми или да предефинира човешкия език, но трябва да е добре за набор от данни с приличен размер. Така или иначе, бих избегнал да съхранявам всеки списък като varchar и да правя "WHERE sequence LIKE '%A%' AND sequence LIKE '%B%'" неща, освен ако абсолютно не можете да се справите с допълнителната работа за добавяне на нови данни.

person tadamson    schedule 09.02.2010
comment
Може да имам голям брой елементи в списъка. JOIN би било твърде скъпо. - person gilbertc; 10.02.2010
comment
@gilbertc - вероятно е по-евтино от сканирането на пълната таблица, което би било необходимо в противен случай - person mmmmmm; 08.05.2012