MySQL, три таблицы: выберите все строки в правой таблице, включая строки, которые не отображаются в средней таблице - PullRequest
3 голосов
/ 12 августа 2011

Моя схема выглядит следующим образом:

Sites S
| S.Id | S.Url |
| 1    | a.com |
| 2    | b.edu |
| 3    | c.org |

SiteFeatures SF
| SF.SiteId | SF.FeatureID |
| 1         | 1            |
| 1         | 2            |
| 1         | 3            |
| 2         | 1            |
| 2         | 2            |
| 2         | 3            |
| 2         | 4            |
| 3         | 2            |
| 3         | 3            |

Features F
| F.Id | F.FeatureName |
| 1    | apple         |
| 2    | banana        |
| 3    | cherry        |
| 4    | diaper        |
| 5    | egg           |
| 6    | fish          |

Я хочу выбрать все сайты, сопоставленные со всеми функциями, включая функции, отсутствующие в средней таблице соединений.Для объектов, которые не имеют записи в таблице соединений, я хочу отобразить «0».Для функций, которые существуют в объединяемой таблице, я хочу получить «1».

Поэтому результаты будут выглядеть следующим образом:

| SiteId | SiteURL | FeatureName | Enabled |
| 1      | a.com   | apple       | 1       |
| 1      | a.com   | banana      | 1       |
| 1      | a.com   | cherry      | 1       |
| 1      | a.com   | diaper      | 0       |
| 1      | a.com   | egg         | 0       |
| 1      | a.com   | fish        | 0       |
| 2      | b.edu   | apple       | 1       |
| 2      | b.edu   | banana      | 1       |
| 2      | b.edu   | cherry      | 1       |
| 2      | b.edu   | diaper      | 1       |
| 2      | b.edu   | egg         | 0       |
| 2      | b.edu   | fish        | 0       |
| 3      | c.org   | apple       | 0       |
| 3      | c.org   | banana      | 1       |
| 3      | c.org   | cherry      | 0       |
| 3      | c.org   | diaper      | 1       |
| 3      | c.org   | egg         | 0       |
| 3      | c.org   | fish        | 0       |

- РЕДАКТИРОВАТЬ - Дополнительная информация.

Первоначально я создал сводную таблицу, используя эту статью:

http://dev.mysql.com/tech-resources/articles/wizard/print_version.html

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

Это утверждение выглядело так:

SELECT
 CONCAT( ", SUM(IF( F.FeatureName = '" , F.FeatureName , "', 1,0 ))" , " AS `" , F.FeatureName , "` ") AS CutNPaste
FROM
  Features F
WHERE 1
GROUP BY F.FeatureName
ORDER BY F.FeatureName
-- END

Второй оператор SQL был следующим:

SELECT
, S.Url
/* This section was dynamically generated, and copied into this SELECT statement */
, SUM(IF( F.FeatureName = 'apple', 1,0 )) AS `apple`
, SUM(IF( F.FeatureName = 'banana', 1,0 )) AS `banana`
, SUM(IF( F.FeatureName = 'cherry', 1,0 )) AS `cherry`
, SUM(IF( F.FeatureName = 'diaper', 1,0 )) AS `diaper`
, SUM(IF( F.FeatureName = 'egg', 1,0 )) AS `egg`
, SUM(IF( F.FeatureName = 'fish', 1,0 )) AS `fish`
/* END of dynamic part */
FROM
  Sites S
LEFT OUTER JOIN SiteFeatures SF ON S.Id = SF.SiteId
LEFT OUTER JOIN Features F ON SF.FeatureId = F.Id
WHERE 1
AND SF.FeatureId = F.Id
AND S.Enabled = 1
GROUP BY S.Url
-- END

Результаты выглядели так:

| Url   | apple | banana | cherry | diaper | egg | fish |
| a.com | 1     | 1      | 1      | 0      | 0   | 0    |
| b.edu | 1     | 1      | 1      | 1      | 0   | 0    |
| c.org | 0     | 1      | 0      | 1      | 0   | 0    |

Я пытался переназначить SQL и понятия в этих двух утверждениях, но я в растерянности.

Ответы [ 3 ]

3 голосов
/ 13 августа 2011

CROSS JOIN можно использовать здесь.(Первый запрос был первым опубликован @nick rulez):

SELECT s.Id   AS SiteId 
     , s.Url  AS SiteURL 
     , f.FeatureName 
     , CASE WHEN sf.SiteID = s.Id 
            THEN 1
            ELSE 0
       END AS Enabled
FROM
    Sites AS s
  CROSS JOIN
    Features AS f
  LEFT JOIN
    SiteFeatures AS sf
      ON  sf.SiteID = s.Id
      AND sf.FeatureID = f.Id

SELECT s.Id   AS SiteId 
     , s.Url  AS SiteURL 
     , f.FeatureName 
     , CASE WHEN EXISTS
                 ( SELECT *
                   FROM SiteFeatures AS sf
                   WHERE sf.SiteID = s.Id
                     AND sf.FeatureID = f.Id
                 ) 
            THEN 1
            ELSE 0
       END AS Enabled
FROM
    Sites AS s
  CROSS JOIN
    Features AS f

SELECT s.Id   AS SiteId 
     , s.Url  AS SiteURL 
     , f.FeatureName 
     , ( SELECT COUNT(*)
         FROM SiteFeatures AS sf
         WHERE sf.SiteID = s.Id
           AND sf.FeatureID = f.Id
       ) AS Enabled
FROM
    Sites AS s
  CROSS JOIN
    Features AS f
1 голос
/ 13 августа 2011
select s.id,s.url,f.featurename,if(sf.featureid is not null,1,0) as enabled
from features as f
cross join sites as s
left join sitefeatures as sf on sf.siteid = s.id and sf.featureid = f.id
order by s.url,f.featurename;


+----+-------+-------------+---------+
| id | url   | featurename | enabled |
+----+-------+-------------+---------+
|  1 | a.com | apple       |       1 |
|  1 | a.com | banana      |       1 |
|  1 | a.com | cherry      |       1 |
|  1 | a.com | diaper      |       0 |
|  1 | a.com | egg         |       0 |
|  1 | a.com | fish        |       0 |
|  2 | b.edu | apple       |       1 |
|  2 | b.edu | banana      |       1 |
|  2 | b.edu | cherry      |       1 |
|  2 | b.edu | diaper      |       1 |
|  2 | b.edu | egg         |       0 |
|  2 | b.edu | fish        |       0 |
|  3 | c.org | apple       |       0 |
|  3 | c.org | banana      |       1 |
|  3 | c.org | cherry      |       1 |
|  3 | c.org | diaper      |       0 |
|  3 | c.org | egg         |       0 |
|  3 | c.org | fish        |       0 |
+----+-------+-------------+---------+
18 rows in set (0.00 sec)
1 голос
/ 12 августа 2011

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Следующее решение написано в MS SQL-Server 2008 TSQL.

Я не верю, что того, чего я не делал, не существует в MySQL, но если что-то, что я использую, не подходит в MySQL, пожалуйста, дайте мне знать, чтобы я мог это исправить. Если кто-то захочет отредактировать и исправить синтаксис в MySQL, мы рады приветствовать вас. Я не знаю, что мне нужно избегать с помощью обратных кавычек и тому подобного, поэтому я не хочу пытаться сделать это и создать плохой код MySQL из правильного кода MS Server.

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

В запросе используется объединение для создания всех возможных комбинаций Site + Feature. Затем он проверяет, существует ли эта комбинация в таблице SiteFeature

--table creation
declare @Sites Table(
    [Id] int not null,
    [Url] varchar(30)
)
INSERT INTO @Sites ([Id],[Url])
     VALUES (1,'a.com'),(2,'b.edu'),(3,'c.org')

declare @Features Table(
    [Id] int not null,
    [Name] varchar(30)
)
INSERT INTO @Features ([Id],[Name])
     VALUES (1,'apple'),(2,'banana'),(3,'cherry'),(4,'diaper'),(5,'egg'),(6,'fish')

declare @SiteFeatures Table(
    [SiteId] int not null,
    [FeatureID] int not null
)
INSERT INTO @SiteFeatures ([SiteId],[FeatureID])
     VALUES (1,1),(1,2),(1,3),(2,1),(2,2),(2,3),(2,4),(3,2),(3,3)

--QUERY HERE
SELECT [SiteId], [SiteURL], [FeatureName], [Enabled] =
    CASE 
        WHEN EXISTS 
            (SELECT * 
             FROM @SiteFeatures SF 
             WHERE SF.SiteId = tmp.SiteId 
                 AND SF.FeatureID = tmp.FeatureId) 
             THEN (1)
             ELSE (0)
        END
    FROM 
    (SELECT S.Id as [SiteId], 
            S.Url as [SiteURL], 
            F.Id as [FeatureId], 
            F.Name as [FeatureName] 
     FROM @Sites S 
    LEFT JOIN @Features F ON (1 = 1)) as tmp 
...