как создать запрос в sql, чтобы нарезать предложения на слова и добавить их в новую таблицу с их частотой - PullRequest
5 голосов
/ 07 мая 2020

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

enter image description here

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

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

enter image description here

любые идеи, это выполнимо, кто-нибудь может помочь, пожалуйста.

Ответы [ 5 ]

5 голосов
/ 10 мая 2020

Если вы используете MySQL 8.0, я бы порекомендовал для этого рекурсивное общее табличное выражение. Идея состоит в том, чтобы итеративно проходить каждое сообщение, по ходу разбивая его на слова. Все, что остается сделать, - это агрегировать.

with recursive cte as (
    select 
        substring(concat(sent, ' '), 1, locate(' ', sent)) word,
        substring(concat(sent, ' '), locate(' ', sent) + 1) sent
    from messages
    union all
    select 
        substring(sent, 1, locate(' ', sent)) word,
        substring(sent, locate(' ', sent) + 1) sent
    from cte
    where locate(' ', sent) > 0
)
select row_number() over(order by count(*) desc, word) wid, word, count(*) freq
from cte 
group by word
order by wid

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

Демо в БД Fiddle

Примеры данных:

sent                       | verif
:------------------------- | ----:
hello my name is alex      |  <em>null</em>
hey alin and alex I'm tom  |  <em>null</em>
hello alex my name is alin |  <em>null</em>

Результаты:

wid | word   | freq
--: | :----- | ---:
  1 | alex   |    3
  2 | alin   |    2
  3 | hello  |    2
  4 | is     |    2
  5 | my     |    2
  6 | name   |    2
  7 | and    |    1
  8 | hey    |    1
  9 | I'm    |    1
 10 | tom    |    1

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

Более простым вариантом было бы поместить запрос в представление, поэтому вы всегда будете в курсе своих данных. Для этого вы можете просто заключить приведенный выше запрос в оператор create view, например:

create view words_view as < above query >;

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

truncate table words;
insert into words < above query >;
3 голосов
/ 10 мая 2020

Perl и PHP и другие имеют гораздо более надежный механизм регулярного выражения для разделения. Я бы использовал один из них, а не SQL.

Я бы использовал пакетные вставки, используя

INSERT INTO words (word, ct)
    VALUES ('this', 1), ('that', 1), ...   -- about 100 words at a time
    ON DUPLICATE KEY UPDATE ct = VALUES(ct) + 1;

CREATE TABLE words (
    word VARCHAR(66) NOT NULL,
    ct MEDIUMINT UNSIGNED NOT NULL,
    PRIMARY KEY(word)
) ENGINE=InnoDB;

Я не вижу необходимости иметь слова и счетчики в отдельных таблицах, и нет необходимости для AUTO_INCREMENT для "word_id". word - отличный «натуральный ПК». Однако вы должны решить, что делать со складыванием регистра и удалением акцента.

Что касается разбиения на слова ... двойные кавычки и некоторые другие символы явно являются границами слов. Но некоторые символы неоднозначны:

' - часть сокращения или цитаты?
. - аббревиатура или конец предложения

Et c.

2 голосов
/ 14 мая 2020

Для последних версий MySQL (8.0.4 и новее) вы можете использовать

SELECT ROW_NUMBER() OVER (ORDER BY COUNT(word) DESC, word) wid, word, COUNT(word) freq 
FROM sentencess 
CROSS JOIN JSON_TABLE( CONCAT('["', REPLACE(sentencess.sent, ' ', '","'), '"]'),
                       "$[*]" COLUMNS( word VARCHAR(254) PATH "$" )
                     ) AS jsontable
GROUP BY word
ORDER BY freq DESC, word;

fiddle

PS. Я не могу воспроизвести порядок вывода, потому что не понимаю критериев упорядочения в группах freq.

2 голосов
/ 07 мая 2020

На основе этого сообщения DBA Stack Exchange я мог представить себе что-то вроде следующего.

Basi c шагов:

  1. Создайте таблицу со списком слов (word_index в моем примере)
  2. Создайте таблицу, содержащую количество слов (word_count в моем примере)
  3. Создайте хранимую процедуру чтобы разбить предложения на слова на основе SPACE (возможно, придется настроить другие пробелы, такие как перенос строк) и записать его в word_index таблицу
  4. вычислить статистику и записать ее в word_count

Пошагово в коде:

Создать word_index:

CREATE TABLE IF NOT EXISTS `word_index` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `word` varchar(150) DEFAULT NULL,
  PRIMARY KEY (`id`)
);

Создать word_count:

CREATE TABLE IF NOT EXISTS `word_count` (
  `word` varchar(150) NOT NULL,
  `occurrences` int(11) DEFAULT NULL,
  PRIMARY KEY (`word`)
)

Создайте процедуру transfer_cell для переноса разделенных слов в целевую таблицу:

DELIMITER //
CREATE FUNCTION `SPLIT_STRING`(val TEXT, delim VARCHAR(12), pos INT) RETURNS text CHARSET latin1
BEGIN
        DECLARE output TEXT;
        SET output = REPLACE(SUBSTRING(SUBSTRING_INDEX(val, delim, pos), CHAR_LENGTH(SUBSTRING_INDEX(val, delim, pos - 1)) + 1), delim, '');
        IF output = '' THEN
            SET output = null;
        END IF;
        RETURN output;
    END//
DELIMITER ;

-- Dumping structure for procedure test.TRANSFER_CELL
DELIMITER //
CREATE PROCEDURE `transfer_cell`()
BEGIN
        DECLARE i INTEGER;
        SET i = 1;
        REPEAT
            INSERT INTO word_index (word)
            SELECT SPLIT_STRING(sent, ' ', i)
            FROM sentences
            WHERE SPLIT_STRING(sent, ' ', i) IS NOT NULL;
            SET i = i + 1;
        UNTIL ROW_COUNT() = 0
        END REPEAT;
    END//
DELIMITER ;

Это основная c установка. Обратите внимание, что я использовал таблицу sentences вместо sentencess с двойными s.

Статистика обновления:

TRUNCATE TABLE word_index;
TRUNCATE TABLE word_count;

CALL transfer_cell();
INSERT INTO word_count
  SELECT word, COUNT(1) occurrences FROM word_index 
  GROUP BY word;

Результат:

Вот скриншот результатов, как описано выше:

Result image

1 голос
/ 13 мая 2020

Предупреждение: это T Sql, а не MySQL.

-- 1. To create a function that splits the sentence into words, and returns the Words Table
-- 2. To insert into your Result Table all of the words Table results
-- 3. Calculate the Frequency

----------[ The Split Function ]
CREATE FUNCTION dbo.udf_SplitString 
        (
                @Sentence   varchar(max)
            ,   @Separator  char(1) 
        )
        RETURNS @WordList TABLE (Word varchar(50)) 
    AS
        BEGIN
            SET @Separator  =   ISNULL(@Separator, ' ') 

            DECLARE @Word   varchar(50)

            SET @Sentence = LTRIM(@Sentence) + @Separator -- Make sure last word has a separator after. Also get rid of leading spaces.

            WHILE   (CHARINDEX(@Separator, @Sentence) > 0)
                BEGIN
                    SET @Word = SUBSTRING(@Sentence, 1, CHARINDEX(@Separator, @Sentence) - 1)
                    INSERT INTO @WordList   SELECT LTRIM(@Word)
                    -- Remove word added to the List from the sentence.
                    SET @Sentence =  SUBSTRING(@Sentence,   CHARINDEX(@Separator, @Sentence) + 1,   LEN(@Sentence))
                    SET @Sentence =  LTRIM(@Sentence)           
            END                 
            RETURN
        END 

----------[ The Script ]
DECLARE     @SentenceList   TABLE   (Sentence varchar(max))
INSERT INTO @SentenceList   VALUES
            ('hello my name is alex')
        ,   ('hey alin and alex I''m tom')  
        ,   ('hello alex my name is alin')

DECLARE     @WordList   TABLE   (Word varchar(50))

INSERT INTO @WordList   
SELECT  
        W.Word
FROM        @SentenceList   S
CROSS APPLY (
                SELECT Word FROM dbo.udf_SplitString(S.Sentence, ' ')
            ) W 

SELECT 
        ID  =   ROW_NUMBER() OVER(ORDER BY SUM(1) DESC, Word)
    ,   Word
    ,   Frequency   =   SUM(1)
FROM @WordList
GROUP BY Word
...