Я новый разработчик SQL, и я пытаюсь написать запрос, который будет получать следующие результаты из трех таблиц в базе данных:

ID Текст Оценка № 1 Оценка № 2 Оценка № 3

Схема для трех таблиц:

T_TextScore Table: Id, TextId, Label, Score, TypeId
T_Text: Id, Text
T_Status Table: Id, Type

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

Вот запрос, который я имею, и я думаю, что он также неэффективен с точки зрения производительности:

SELECT        T_TextScore.TextId, T_Text.Text, T_TextScore.Label, T_TextScore.Score
FROM            T_TextScore INNER JOIN
                         T_Text ON T_TextScore.TextId = T_Text.Id
WHERE        (T_TextScore.TypeId = 3)
UNION
SELECT        T_TextScore.TextId, T_Text.Text, T_TextScore.Label, T_TextScore.Score
FROM            T_TextScore INNER JOIN
                         T_Text ON T_TextScore.TextId = T_Text.Id
WHERE        (T_TextScore.TypeId = 4)
UNION
SELECT        T_TextScore.TextId, T_Text.Text, T_TextScore.Label, T_TextScore.Score
FROM            T_TextScore INNER JOIN
                         T_Text ON T_TextScore.TextId = T_Text.Id
WHERE        (T_TextScore.TypeId = 5);

ОБНОВЛЕНИЕ: После использования запроса, предложенного @Craig Young, я получил две строки для каждого текста, и я не знаю почему. Не могли бы вы объяснить мне, почему?

введите описание изображения здесь

1
Tech Lover 28 Май 2017 в 18:55

2 ответа

Лучший ответ

Ваш запрос не выполняет то, что вы просили. Следующее было бы намного лучшим способом сделать то, что вы делаете в настоящее время:

SELECT  ts.TextId, t.Text, ts.Label, ts.Score
FROM    T_TextScore ts /* Table alias makes query much more readable */
        INNER JOIN T_Text t ON
            ts.TextId = t.Id
WHERE   ts.TypeId IN (3, 4, 5)

Тем не менее, первая часть вашего вопроса предполагает, что вы действительно хотите изменить данные?

Если это так, вы можете использовать синтаксис PIVOT, Или ручное вращение:

SELECT  ts.TextId,
        /* Use aggregate function to get only 1 per TextId */
        MAX(t.Text) AS Text, MAX((ts.Label) AS Label,
        /* Simply move ts.Score to the correct column to be summed. */
        SUM(CASE WHEN ts.TypeId = 3 THEN ts.Score ELSE 0 END) AS Score3,
        SUM(CASE WHEN ts.TypeId = 4 THEN ts.Score ELSE 0 END) AS Score4,
        SUM(CASE WHEN ts.TypeId = 5 THEN ts.Score ELSE 0 END) AS Score5
FROM    T_TextScore ts
        INNER JOIN T_Text t ON
            ts.TextId = t.Id
WHERE   ts.TypeId IN (3, 4, 5)
GROUP BY ts.TextId

ПРИМЕЧАНИЕ: синтаксис PIVOT немного более лаконичен. Но как ни странно, я видел, что он иногда работает немного медленнее, чем ручной поворот. Так что, если производительность важна, вам придется сравнивать.


На основании вашего комментария:

После использования запроса, предложенного @Craig Young, я получил две строки для каждого текста, и я не знаю почему.

Вы, вероятно, либо удалили предложение GROUP BY, либо включили Text и Label в GROUP BY. Это заставило меня понять, что я забыл иметь дело с этими двумя столбцами, которые не были частью ни агрегата, ни GROUP BY.

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

1
Disillusioned 29 Май 2017 в 00:28

Вы хотите условную агрегацию. Моя лучшая догадка просто:

SELECT ts.TextId, 
       MAX(CASE WHEN ts.TypeId = 3 THEN ts.Score END) as Score_1,
       MAX(CASE WHEN ts.TypeId = 4 THEN ts.Score END) as Score_2,
       MAX(CASE WHEN ts.TypeId = 5 THEN ts.Score END) as Score_3
FROM T_TextScore ts 
GROUP BY ts.TextId;
2
Gordon Linoff 28 Май 2017 в 16:07