Как решить этот запрос MySQL? - PullRequest
0 голосов
/ 04 мая 2011

У меня есть таблица, которая выглядит так:

    CREATE TEMPORARY TABLE MainList (
  `pTime` int(10) unsigned NOT NULL,
  `STD` double NOT NULL,
  PRIMARY KEY (`pTime`)
) ENGINE=MEMORY;


+------------+-------------+
| pTime      | STD         |
+------------+-------------+
| 1106080500 |  -0.5058072 |
| 1106081100 | -0.82790455 |
| 1106081400 | -0.59226294 |
| 1106081700 | -0.99998194 |
| 1106540100 | -0.86649279 |
| 1107194700 |  1.51340543 |
| 1107305700 |  0.96225296 |
| 1107306300 |  0.53937716 |
+------------+-------------+ .. etc

pTime - мой первичный ключ.

Я хочу сделать запрос, который для каждой строки в моей таблице найдет первый pTime, где STD имеет перевернутый знак и находится дальше от 0, чем STD приведенной выше таблицы. (Для простоты просто представьте, что я ищу 0-STD)

Вот пример вывода, который я хочу:

+------------+-------------+------------+-------------+
| pTime      | STD         | pTime_Oppo | STD_Oppo    |
+------------+-------------+------------+-------------+
| 1106080500 |  -0.5058072 | 1106090400 |  0.57510881 |
| 1106081100 | -0.82790455 | 1106091300 |  0.85599817 |
| 1106081400 | -0.59226294 | 1106091300 |  0.85599817 |
| 1106081700 | -0.99998194 | 1106091600 |  1.0660959  |
+------------+-------------+------------+-------------+

Я не могу понять это правильно! Я попробовал следующее:

SELECT DISTINCT
    MainList.pTime,
    MainList.STD,
    b34d1.pTime,
    b34d1.STD
FROM
    MainList
JOIN b34d1 ON(
    b34d1.pTime > MainList.pTime
    AND(
        (
            MainList.STD > 0
            AND b34d1.STD <= 0 - MainList.STD
        )
        OR(
            MainList.STD < 0
            AND b34d1.STD >= 0 - MainList.STD
        )
    )
);

Этот код просто останавливает мой сервер.

P.S. Таблица b34d1 аналогична MainList, за исключением того, что она содержит гораздо больше элементов:

mysql>  select STD, Slope from b31d1 limit 10;
+-------------+--------------+
| STD         | Slope        |
+-------------+--------------+
| -0.44922675 |   -5.2016129 |
| -0.11892021 |  -8.15249267 |
|  0.62574686 | -10.19794721 |
|  1.10469057 | -12.43768328 |
|  1.52917352 | -13.08651026 |
|  1.61803899 |  -13.2441349 |
|  1.82686555 | -12.04912023 |
|  2.07480736 | -11.22067449 |
|  2.45529961 |  -7.84090909 |
|  1.86468335 |  -6.26466276 |
+-------------+--------------+
mysql>  select count(*) from b31d1;
+----------+
| count(*) |
+----------+
|   439340 |
+----------+

1 строка в наборе (0,00 с)

На самом деле MainList - это просто отфильтрованная версия b34d1, использующая механизм MEMORY

mysql> show create table b34d1;
+-------+-----------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------+
| Table | Create Table
                                                                                                       |
+-------+-----------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------+
| b34d1 | CREATE TABLE `b34d1` (
  `pTime` int(10) unsigned NOT NULL,
  `Slope` double NOT NULL,
  `STD` double NOT NULL,
  PRIMARY KEY (`pTime`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 MIN_ROWS=339331 MAX_ROWS=539331 PACK_KEYS=1 ROW_FORMAT=FIXED |
+-------+-----------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------+

Edit: я только что провел небольшой эксперимент, и я очень смущен результатами:

SELECT DISTINCT
    b34d1.pTime,
    b34d1.STD,
    Anti.pTime,
    Anti.STD

FROM
    b34d1

LEFT JOIN b34d1 As Anti ON(
    Anti.pTime > b34d1.pTime
    AND(
        (
            b34d1.STD > 0
            AND b34d1.STD <= 0 - Anti.STD
        )
        OR(
            b34d1.STD < 0
            AND b34d1.STD >= 0 - Anti.STD
        )
    )
)  limit 10;

+------------+-------------+------------+------------+
| pTime      | STD         | pTime      | STD        |
+------------+-------------+------------+------------+
| 1104537600 | -0.70381962 | 1104539100 | 0.73473692 |
| 1104537600 | -0.70381962 | 1104714000 | 1.46733274 |
| 1104537600 | -0.70381962 | 1104714300 | 2.02097356 |
| 1104537600 | -0.70381962 | 1104714600 | 2.60642099 |
| 1104537600 | -0.70381962 | 1104714900 | 2.01006557 |
| 1104537600 | -0.70381962 | 1104715200 | 1.97724189 |
| 1104537600 | -0.70381962 | 1104715500 | 1.85683704 |
| 1104537600 | -0.70381962 | 1104715800 |  1.2754127 |
| 1104537600 | -0.70381962 | 1104716100 | 0.87900156 |
| 1104537600 | -0.70381962 | 1104716400 | 0.72957739 |
+------------+-------------+------------+------------+

Почему все значения первого pTime одинаковы?

Ответы [ 3 ]

1 голос
/ 04 мая 2011

Выбор других полей из строки, имеющей некоторую совокупную статистику (например, минимальное или максимальное значение), немного запутан в SQL.Такие запросы не так просты.Обычно вам требуется дополнительное объединение или подзапрос.Например:

SELECT m.pTime, m.STD, m2.pTime AS pTime_Oppo, m2.STD AS STD_Oppo
  FROM MainList AS m
    JOIN 
      (SELECT m1.pTime, MIN(m2.pTime) AS pTime_Oppo
         FROM MainList AS m1
           JOIN MainList AS m2 
             ON m1.pTime < m2.pTime AND SIGN(m1.STD) != SIGN(m2.STD)
         WHERE ABS(m1.STD) <= ABS(m2.std)
         GROUP BY m1.pTime
      ) AS oppo ON m.pTime = oppo.pTime
    JOIN MainList AS m2 ON oppo.pTime_Oppo = m2.pTime
;

Использование данных примера:

INSERT INTO MainList (`pTime`, `STD`)
  VALUES
(1106080500, -0.5058072),
(1106081100, -0.82790455),
(1106081400, -0.59226294),
(1106081700, -0.99998194),
(1106090400,  0.57510881),
(1106091300,  0.85599817),
(1106091600,  1.0660959),
(1106540100, -0.86649279),
(1107194700,  1.51340543),
(1107305700,  0.96225296),
(1107306300,  0.53937716),
;

Результаты:

+------------+-------------+------------+-------------+
| pTime      | STD         | pTime_Oppo | STD_Oppo    |
+------------+-------------+------------+-------------+
| 1106080500 |  -0.5058072 | 1106090400 |  0.57510881 |
| 1106081100 | -0.82790455 | 1106091300 |  0.85599817 |
| 1106081400 | -0.59226294 | 1106091300 |  0.85599817 |
| 1106081700 | -0.99998194 | 1106091600 |   1.0660959 |
| 1106090400 |  0.57510881 | 1106540100 | -0.86649279 |
| 1106091300 |  0.85599817 | 1106540100 | -0.86649279 |
| 1106540100 | -0.86649279 | 1107194700 |  1.51340543 |
+------------+-------------+------------+-------------+
0 голосов
/ 04 мая 2011
SELECT
  m.pTime,
  m.STD,
  mo.pTime AS pTime_Oppo,
  -mo.STD AS STD_Oppo
FROM MainList m
  INNER JOIN (
    SELECT
      pTime,
      -STD AS STD
    FROM MainList
  ) mo ON m.STD > 0 AND mo.STD > m.STD
       OR m.STD < 0 AND mo.STD < m.STD
  LEFT JOIN (
    SELECT
      pTime,
      -STD AS STD
    FROM MainList
  ) mo2 ON mo.STD > 0 AND mo2.STD > m.STD AND mo.STD > mo2.STD
        OR mo.STD < 0 AND mo2.STD < m.STD AND mo.STD < mo2.STD
WHERE mo2.pTime IS NULL
0 голосов
/ 04 мая 2011

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

Вы создаете временную таблицу внутри SP, так что вы можете изменять ее схему, не теряя ничего, добавляя столбец, в котором хранится знак STD, и сохраняя сам STD без знака, вы получите ОГРОМНОЕ повышение производительности, потому что вы можете просто найти первый больший pTime. и больше STD с другим знаком, и все условия могут использовать индексы в запросе, подобном этому (STD_positive сохраняет знак STD):

SELECT * from mainlist m
LEFT JOIN mainlist mu 
ON mu.pTime = ( SELECT md.pTime FROM mainlist md 
            WHERE m.pTime < md.pTime
            AND m.STD < md.STD
            AND m.STD_positive <> md.STD_positive
            ORDER BY md.pTime
            LIMIT 1 ) 

Здесь требуется LEFT JOIN, чтобы возвращать строки, которые не имеют больше STD. Если они вам не нужны, используйте простой JOIN. Этот запрос должен нормально выполняться даже для большого количества записей с правильными индексами, основанными на тщательной проверке вывода EXPLAIN, начиная с индекса на STD.

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