-1
1|24-jan-11|n1|89|17|81|6|40
2|24-jan-11|n1|21|15|42|67|11
3|24-jan-11|n1|31|17|45|70|69
4|24-jan-11|n1|74|88|47|56|14

5|28-jan-11|n2|31|25|75|37|84
6|28-jan-11|n2|15|4|20|34|68
7|28-jan-11|n2|19|15|81|14|67
8|28-jan-11|n2|47|17|15|71|14

毎日の数値の配列を含む MySQL テーブルがあります (上記は 2011 年 1 月 24 日と 2011 年 1 月 28 日の数値を示しています)。各数値は の間の任意の数値1 and 90です。2 日に共通する同じ行の数のカップルを見つける必要があります。

例えば:

row #2 (January 24, 2011) contains 15 and 67
row #7 (January 28, 2011) also contains 15 and 67

row #4 (January 24, 2011) contains 47 and 14
row #8 (January 28, 2011) also contains 47 and 14

スクリプトは次を返す必要があります。

"15" and "67" in the row "2" and "7"
"47" and "14" in the row "4" and "8"

私の解決策は、テーブル内のすべての数値を PHP ループで解析することでした。問題は、時間がかかり、サーバーがクラッシュすることです。

これを達成するために使用できる数式または簡単な PHP/mySQL 関数はありますか?

4

3 に答える 3

2

楽しみのために純粋なSQLで解決しました。十分なパフォーマンスがあるかどうかを判断するのはあなた次第です:)

テストデータ:

CREATE TABLE yourTable
    (`id` int, `date` varchar(9), `col1` varchar(2), `col2` int, `col3` int, `col4` int, `col5` int, `col6` int)
;

INSERT INTO yourTable
    (`id`, `date`, `col1`, `col2`, `col3`, `col4`, `col5`, `col6`)
VALUES
    (1, '24-jan-11', 'n1', 89, 17, 81, 6, 40),
    (2, '24-jan-11', 'n1', 21, 15, 42, 67, 11),
    (3, '24-jan-11', 'n1', 31, 17, 45, 70, 69),
    (4, '24-jan-11', 'n1', 74, 88, 47, 56, 14),
    (5, '28-jan-11', 'n2', 31, 25, 75, 37, 84),
    (6, '28-jan-11', 'n2', 15, 4, 20, 34, 68),
    (7, '28-jan-11', 'n2', 19, 15, 81, 14, 67),
    (8, '28-jan-11', 'n2', 47, 17, 15, 71, 14)
;

そして、ここに来ます:

select
yt1.id, yt2.id,
case when yt1.col2 in (yt2.col2, yt2.col3, yt2.col4, yt2.col5, yt2.col6) then yt1.col2 else null end c1,
case when yt1.col3 in (yt2.col2, yt2.col3, yt2.col4, yt2.col5, yt2.col6) then yt1.col3 else null end c2,
case when yt1.col4 in (yt2.col2, yt2.col3, yt2.col4, yt2.col5, yt2.col6) then yt1.col4 else null end c3,
case when yt1.col5 in (yt2.col2, yt2.col3, yt2.col4, yt2.col5, yt2.col6) then yt1.col5 else null end c4,
case when yt1.col6 in (yt2.col2, yt2.col3, yt2.col4, yt2.col5, yt2.col6) then yt1.col6 else null end c5
from
yourTable yt1
,yourTable yt2 
where
yt1.date = '24-jan-11'
and yt2.date = '28-jan-11'
and
(
yt1.col2 in (yt2.col2, yt2.col3, yt2.col4, yt2.col5, yt2.col6)
or yt1.col3 in (yt2.col2, yt2.col3, yt2.col4, yt2.col5, yt2.col6)
or yt1.col4 in (yt2.col2, yt2.col3, yt2.col4, yt2.col5, yt2.col6)
or yt1.col5 in (yt2.col2, yt2.col3, yt2.col4, yt2.col5, yt2.col6)
or yt1.col6 in (yt2.col2, yt2.col3, yt2.col4, yt2.col5, yt2.col6)
)
having 
case when c1 is null then 0 else 1 end 
+ case when c2 is null then 0 else 1 end 
+ case when c3 is null then 0 else 1 end 
+ case when c4 is null then 0 else 1 end 
+ case when c5 is null then 0 else 1 end 
>= 2
于 2013-04-08T09:44:35.753 に答える
0

「数学の公式はありますか...」いいえ-数字がどのようになったのかについてのルールはありません。これを行う唯一の方法は、先に進んですべての組み合わせを比較することです。とはいえ、処理を高速化するためにできることはあります。同様に、スクリプトを停止させる落とし穴もあります。作業するためのコードが提供されていないので、手続き的な解決策を提示します。


ゲームのルール

  • 私が理解しているように、2 つの変数 (配列) があり、1 日ごとに 1 つです。
  • 「n1」と「n2」の意味がわからないので無視します。
  • 2 つの日/日付間の共通の値のみに関心があり、同じ日付内にある可能性のあるペアは気にしません。

$day[0]この例では、あなたの日は変数によって表されます$day[1]- したがって、同じことを繰り返さないように (そして、時間のかかるループに陥ることはありません)、おそらく、いずれかの日を反復して、ペアのすべての可能な組み合わせを計算する必要があります。各行。検索を容易にするために、これらの組み合わせは配列キーとして使用され、対応する行 ID にマップされます。例えば。

$pairs = array();
foreach($days[0] as $day){
  $len = count($day);
  for($i=3;$i<$len;$i++)
    for($j=$i+1;$j<$len;$j++){
      $key = $day[$j] > $day[$i] ? "{$day[$i]}|{$day[$j]}" 
                                 : "{$day[$j]}|{$day[$i]}";
      $pairs[$key] = $day[0];
    }
} 

組み合わせが「見つかった」順序に応じて配列キーを複製する必要がないように、キーを番号順に設定するように指定したことに注意してください。この事前に計算されたペアの配列を取得すると、2 日目にパスを作成し、共通の値を特定することが容易になります。例えば。

foreach($days[1] as $day){
  $len = count($day);
  for($i=3;$i<$len;$i++)
    for($j=$i+1;$j<$len;$j++){
      $key = $day[$j] > $day[$i] ? "{$day[$i]}|{$day[$j]}" 
                                 : "{$day[$j]}|{$day[$i]}";
      if(isset($pairs[$key]))
        echo "\"{$day[$i]}\" and \"{$day[$j]}\" in the row "
          .  "\"{$pairs[$key]}\" and \"{$day[0]}\"<br/>";
    }
}

実施例

ソリューションをきれいにすることはあなたに任せます。大規模なデータセットにどのようにスケーリングするかはわかりませんが、作業するのに十分な量を提供しました-set_time_limit特に大きな配列を使用している場合は、いつでも利用できます.

于 2013-04-08T09:43:13.770 に答える
0

それの純粋な SQL バージョン。楽しみのために、トムボンのように。

これは、元の投稿が示唆するように、データが単一のパイプで区切られたフィールドにあることを前提としています。

これは単一の SQL であり、追加の整数テーブル (整数と呼ばれ、i と呼ばれる 1 列と、0 から 9 までの値を持つ 10 行) に依存しています。

SELECT DISTINCT SubA.TheDate, SubB.TheDate, SubA.TheRowNum, SubB.TheRowNum, SubA.aDelimitedSection, SubB.aDelimitedSection, SubC.aDelimitedSection, SubD.aDelimitedSection,
CONCAT('"', SubA.aDelimitedSection, '" and "', SubC.aDelimitedSection, '" in the row "', SubA.TheRowNum, '" and "', SubB.TheRowNum, '"')
FROM (SELECT SUBSTRING_INDEX(SomeField, '|', 1) TheRowNum, SUBSTRING_INDEX(SUBSTRING_INDEX(SomeField, '|', 2), '|', -1) TheDate, SomeField, SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(SomeField, '|'), '|', Sub1.AnInt), '|', -1) AS aDelimitedSection, Sub1.AnInt
FROM StatsTest,
(SELECT 4+a.i+b.i*10 AS AnInt FROM integers a, integers b) Sub1
WHERE Sub1.AnInt <= (1 + LENGTH(SomeField) - LENGTH( REPLACE ( SomeField, "|", "")))) SubA
INNER JOIN (SELECT SUBSTRING_INDEX(SomeField, '|', 1) TheRowNum, SUBSTRING_INDEX(SUBSTRING_INDEX(SomeField, '|', 2), '|', -1) TheDate, SomeField, SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(SomeField, '|'), '|', Sub1.AnInt), '|', -1) AS aDelimitedSection, Sub1.AnInt
FROM StatsTest,
(SELECT 4+a.i+b.i*10 AS AnInt FROM integers a, integers b) Sub1
WHERE Sub1.AnInt <= (1 + LENGTH(SomeField) - LENGTH( REPLACE ( SomeField, "|", "")))) SubB
ON SubA.aDelimitedSection = SubB.aDelimitedSection AND SubA.TheRowNum < SubB.TheRowNum AND SubA.TheDate != SubB.TheDate
INNER JOIN (SELECT SUBSTRING_INDEX(SomeField, '|', 1) TheRowNum, SUBSTRING_INDEX(SUBSTRING_INDEX(SomeField, '|', 2), '|', -1) TheDate, SomeField, SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(SomeField, '|'), '|', Sub1.AnInt), '|', -1) AS aDelimitedSection, Sub1.AnInt
FROM StatsTest,
(SELECT 4+a.i+b.i*10 AS AnInt FROM integers a, integers b) Sub1
WHERE Sub1.AnInt <= (1 + LENGTH(SomeField) - LENGTH( REPLACE ( SomeField, "|", "")))) SubC
ON SubA.aDelimitedSection < SubC.aDelimitedSection AND SubA.TheRowNum = SubC.TheRowNum
INNER JOIN (SELECT SUBSTRING_INDEX(SomeField, '|', 1) TheRowNum, SUBSTRING_INDEX(SUBSTRING_INDEX(SomeField, '|', 2), '|', -1) TheDate, SomeField, SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(SomeField, '|'), '|', Sub1.AnInt), '|', -1) AS aDelimitedSection, Sub1.AnInt
FROM StatsTest,
(SELECT 4+a.i+b.i*10 AS AnInt FROM integers a, integers b) Sub1
WHERE Sub1.AnInt <= (1 + LENGTH(SomeField) - LENGTH( REPLACE ( SomeField, "|", "")))) SubD
ON SubC.aDelimitedSection = SubD.aDelimitedSection AND SubB.TheRowNum = SubD.TheRowNum

私のマシンのxamppでテストデータを使用すると0.014秒かかりますが、実際にスケーラブルかどうかはわかりません

于 2013-04-08T11:09:11.520 に答える