分组和交集的Sql问题

时间:2011-09-07 22:16:23

标签: mysql sql

我在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 个答案:

答案 0 :(得分:4)

具体答案......

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)

注意:

  • 小表
  • 不需要此优化
  • 此优化假设您有两个索引(sourceName,ActionName)AND(actionName,sourceName)
  • 这是一个很好的例子,我用它来表明更多代码可以更快

答案 1 :(得分:3)

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'
      )

答案 2 :(得分:0)

也许我没有得到你的问题,但如果结果中你想要的只是你所展示的内容,你就不需要做任何事情或任何事情。

select distinct ActionName from YourTable 
where SourceName in ('S01', 'S02')