Sql проблема с группировкой и пересечением - PullRequest
1 голос
/ 08 сентября 2011

У меня есть эта таблица журнала в MySQL со столбцами ActionName и SourceName.

Одни и те же действия могут быть зарегистрированы несколько раз из разных источников.

Так что пример таблицы может выглядеть как

ActionName    SourceName
----------------------------
Add           S01
Add           S02
Add           S02
Edit          S01
Edit          S01
Delete        S01
Delete        S02

Теперь я хотел бы запросить эту таблицу и найти действия, которые были выполнены как S01, так и S02.Таким образом, результаты будут такими:

 ActioName
--------------
Add
Delete

Как бы я решил это с помощью SQL?

Ответы [ 3 ]

4 голосов
/ 08 сентября 2011

Конкретный ответ ...

SELECT
  ActionName
FROM
  yourTable
WHERE
  SourceName in ('S01', 'S02')
GROUP BY
  ActionName
HAVING
  COUNT(DISTINCT SourceName) = 2


Возможно, быстрее для вашего конкретного вопроса ...

SELECT
  a.SourceName
FROM
  yourTable  AS a
INNER JOIN
  yourTable  AS b
    ON a.ActionName = b.ActionName
WHERE
      a.SourceName = 'S01'
  AND b.SourceName = 'S02'


Общий ответ ...

SELECT
  ActionName
FROM
  yourTable
INNER JOIN
  tableWithSourceNames
    ON yourTable.SourceName = tableWithSourceNames.SourceName
GROUP BY
  ActionName
HAVING
  COUNT(DISTINCT yourTable.SourceName) = (SELECT COUNT(DISTINCT SourceName) FROM tableWithSourceNames)


Оказывается, это очень плохо масштабируется (поскольку размер вашей таблицы увеличивается, производительность падает). Вы можете оптимизировать, хотя ...

Имея немного метаданных о том, насколько избирательным является каждое SourceName ...

CREATE TABLE sourceNameMetaData (
  sourceName  VARCHAR(64),
  occurances  INT
)

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

SELECT
  yourTable.ActionName
FROM
(
  SELECT
    ActionName
  FROM
  (
    SELECT
      sourceName
    FROM
      sourceNameMetaData
    INNER JOIN
      tableWithSourceNames
        ON tableWithSourceNames.SourceName = sourceNameMetaData.SourceName
    ORDER BY
      occurances ASC
    LIMIT
      1
  )
    AS filter    
  INNER JOIN
    yourTable
      ON yourTable.SourceName = filter.SourceName
  GROUP BY
    ActionName
)
  AS filter
INNER JOIN
  yourTable
    ON yourTable.ActionName = filteredData.ActionName
INNER JOIN
  tableWithSourceNames
    ON yourTable.SourceName = tableWithSourceNames.SourceName
GROUP BY
  yourTable.ActionName
HAVING
  COUNT(DISTINCT yourTable.SourceName) = (SELECT COUNT(DISTINCT SourceName) FROM tableWithSourceNames)

Примечания:

  • Эта оптимизация не нужна для небольших таблиц
  • Эта оптимизация предполагает, что у вас есть индексы ОБА (имя-источника, имя-действия) И (имя-действия, имя-источника)
  • Это замечательный пример, который я использую, чтобы показать, что больше кода МОЖЕТ быть быстрее
3 голосов
/ 08 сентября 2011
SELECT ActionName 
FROM LogTable 
WHERE SourceName IN ('S01', 'S02')
GROUP BY ActionName
HAVING COUNT(DISTINCT SourceName) = 2

или

SELECT ActionName 
FROM 
    ( SELECT DISTINCT ActionName
      FROM LogTable
    ) AS dn 
WHERE 
    EXISTS
      ( SELECT *
        FROM LogTable AS a
        WHERE a.ActionName = dn.ActionName
          AND a.SourceName = 'S01'
      )
  AND
    EXISTS
      ( SELECT *
        FROM LogTable AS b
        WHERE b.ActionName = dn.ActionName
          AND b.SourceName = 'S02'
      )
0 голосов
/ 08 сентября 2011

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

select distinct ActionName from YourTable 
where SourceName in ('S01', 'S02')
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...