Есть ли какой-либо запрос / код, который может найти общие значения в записях? - PullRequest
0 голосов
/ 31 октября 2018

Кто-нибудь может подсказать мне (может быть, простой и быстрый запрос, если есть, или какой-то быстрый код), чтобы преобразовать мой файл данных CSV (с разделением запятыми):

1,A,C,Z,F,G
2,G,Q,R,C,
3,Z,G,Q,
4,C,F,
5,O,P,
6,O,X,Y,J,
7,A,P,X,

У меня есть эта таблица с ~ 1 000 000 записей как эти 7 записей, которые вы видите (в реальной базе данных A, B, C, ... являются словами в строке), записи 1 и 2 являются общими для значений G и C, а также 2,3 и 1,3 и ...

Я хочу синхронизировать записи, если они имеют как минимум два общих значения, таких как записи 1 и 2,3,4 (но записи 5,6,7 не имеют по крайней мере 2 общих значения с другими), и создать такой список :

1 A C Z F G Q R
2 G Q R C A Z F
3 Z G Q A C F R
4 C F A Z G Q R
5 O P
6 O X Y J
7 A P X

в конце мы должны иметь 4 одинаковые записи, если мы сортируем данные, и еще одну без синхронизации:

1 A C F G Q R Z
2 A C F G Q R Z
3 A C F G Q R Z
4 A C F G Q R Z
5 O P
6 J O X Y
7 A P X

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

1 A C Z F G 
2 G Q R C

запись 1 имеет C и G, общие с Record 2, теперь 1 не имеет R и Q, поэтому у нас должно быть 1 ACZFG + Q и R, а у Record 2 нет A, Z и F, таким образом, мы должны иметь: 2 GQRC + A, Z и F, таким образом, в конце мы имеем:

1 A C Z F G Q R 
2 G Q R C A Z F

Мне нужны все записи соответственно в очереди сверху вниз. написал код Delphi, но это так медленно. Кто-то предложил мне этот отличный код:

def f=[:]
new File('Data.csv').readLines().each{
def items=it.split(',')
def name
items.eachWithIndex { String entry, int i -> 
    if(i==0){
        name=entry
    }
    else if(entry){
        if(!f[entry])
            f[entry]=[]
        f[entry]<<name
    }
 } 

}
f.findAll {it.value.size()>1}

Это очень быстро (из-за использования файла карты, я думаю), но находит только общие значения.

1 Ответ

0 голосов
/ 03 ноября 2018

Если вы захотите использовать решение SQL, тогда эти данные CSV могут быть положить в нормализованную таблицу с развернутыми данными по ID и СЛОВО.

Если у вас есть это, это становится вопросом самостоятельного присоединения к этому столу. И объединить слова обратно в алфавитном порядке.

Тест SqlFiddle здесь

Не уверен, насколько быстрым будет этот метод для таблицы с 1000k записей.
Но это интересная головоломка.

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

DROP TABLE IF EXISTS test_words;
CREATE TABLE IF NOT EXISTS test_words (
  id int unsigned NOT NULL PRIMARY KEY,
  words varchar(60) NOT NULL
);
INSERT INTO test_words (id, words) VALUES
(1,'A C Z F G'),
(2,'G Q R C'),
(3,'Z G Q'),
(4,'C F'),
(5,'P O'),
(6,'O X Y J'),
(7,'A P X');

Таблица подсчета с номерами:

DROP TABLE IF EXISTS tmp_numbers;
CREATE TEMPORARY TABLE IF NOT EXISTS tmp_numbers (
  n int unsigned NOT NULL PRIMARY KEY
);
INSERT INTO tmp_numbers (n) VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);

Развернуть слова:

DROP TABLE IF EXISTS test_words_unfolded;
CREATE TABLE test_words_unfolded (
  word varchar(10) NOT NULL,
  id int unsigned NOT NULL,
  PRIMARY KEY (word, id)
);
INSERT INTO test_words_unfolded (word, id)
SELECT DISTINCT SUBSTRING_INDEX(SUBSTRING_INDEX(t.words,' ', nr.n),' ',-1) as word, t.id
FROM test_words AS t
JOIN tmp_numbers AS nr
  ON CHAR_LENGTH(t.words) - CHAR_LENGTH(REPLACE(t.words,' ','')) >= nr.n - 1
 AND SUBSTRING_INDEX(SUBSTRING_INDEX(t.words,' ', nr.n),' ',-1) != '';

Таблица результатов:

DROP TABLE IF EXISTS test_result;
CREATE TABLE IF NOT EXISTS test_result (
  id int unsigned NOT NULL PRIMARY KEY,
  words varchar(60) NOT NULL
);

INSERT INTO test_result (id, words) 
SELECT q.id, GROUP_CONCAT(DISTINCT t3.word ORDER BY t3.word ASC SEPARATOR ' ') as words
FROM 
(
    SELECT t1.id, t2.id as id2
    FROM test_words_unfolded t1
    JOIN test_words_unfolded t2 ON t1.word = t2.word
    GROUP BY t1.id, t2.id
    HAVING COUNT(*) > 1 OR t1.id = t2.id
) q
LEFT JOIN test_words_unfolded t3 ON t3.id = q.id2
GROUP BY q.id
ORDER BY q.id;

SELECT *
FROM test_result
ORDER BY id;

Результат:

id  words
--  -----
1   A C F G Q R Z
2   A C F G Q R Z
3   A C F G Q R Z
4   A C F G Z
5   O P
6   J O X Y
7   A P X

Extra

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

SELECT 
 q2.id, 
 GROUP_CONCAT(DISTINCT CASE WHEN q2.ori = 1 THEN q2.word ELSE CONCAT('[',q2.word,']') END ORDER BY q2.word ASC SEPARATOR ' ') as words
FROM
(
  SELECT
   q1.id, t3.word,
   MAX(CASE WHEN q1.id = t3.id THEN 1 ELSE 0 END) as ori
  FROM 
  (
      SELECT
       t1.id, t2.id as id2
      FROM test_words_unfolded t1
      JOIN test_words_unfolded t2 ON t1.word = t2.word
      GROUP BY t1.id, t2.id
      HAVING COUNT(*) > 1 OR t1.id = t2.id
  ) q1
  LEFT JOIN test_words_unfolded t3 ON t3.id = q1.id2
  GROUP BY q1.id, t3.word
) q2
GROUP BY q2.id
ORDER BY q2.id;

Результат:

id  words
--  -----
1   A C F G [Q] [R] Z
2   [A] C [F] G Q R [Z]
3   [A] [C] [F] G Q [R] Z
4   [A] C F [G] [Z]
5   O P
6   J O X Y
7   A P X

Дополнительный эксперимент здесь

...