SQL - переставить таблицу с помощью запроса - PullRequest
2 голосов
/ 05 мая 2010

У меня плохо спроектированная таблица, которую я унаследовал. Похоже:

    User  Field   Value
    -------------------
    1      name   Aaron
    1      email  aaron@company.com
    1      phone  800-555-4545
    2      name   Mike
    2      email  mike@group.org
    2      phone  777-123-4567
    (etc, etc)

Я бы хотел извлечь эти данные с помощью запроса в более разумном формате:

User  Name   Email              Phone
-------------------------------------------
1     Aaron  aaron@company.com  800-555-4545
2     Mike   mike@group.org     777-123-4567

Я новичок в SQL, но пробовал несколько запросов с вариантами Group By, все без чего-либо, даже близко к успеху.

Есть ли метод SQL, чтобы сделать это легко?

Ответы [ 6 ]

4 голосов
/ 05 мая 2010

это не «плохо спроектированный стол»; но на самом деле таблица значений атрибутов сущности (EAV). К сожалению, реляционные базы данных являются плохой платформой для реализации таких таблиц и сводят на нет большинство приятных моментов СУБД. Распространенный случай использования неправильной лопаты для ввинчивания винта.

но я думаю, что это сработает (на основании ответа Маркуса Адамса , который, как мне кажется, не сработает (редактировать: теперь это работает))

SELECT User1.Value AS name, User2.Value AS email, User3.Value AS phone
FROM Users User1
LEFT JOIN Users User2
  ON User2.User = User1.User AND User2.Field='email'
LEFT JOIN Users User3
  ON User3.User = User1.User AND User3.Field='phone'
WHERE User1.Field = 'name'
ORDER BY User1.User

Edit: получил некоторые тонкости из других ответов (LEFT Joins и имена полей в предложениях ON), теперь кто-нибудь знает, как поставить оставшееся WHERE немного выше? (но не при первом включении JOIN, это слишком уродливо), конечно, это не имеет значения, так как оптимизатор запросов все равно возвращает его обратно.

1 голос
/ 05 мая 2010

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

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

DECLARE @Results TABLE
(
    UserID INT
    , Name VARCHAR(50)
    , Email VARCHAR(50)
    , Phone VARCHAR(50)
)

INSERT INTO @Results
    SELECT DISTINCT User FROM UserValues

UPDATE
    R
SET
    R.Name = UV.Value
FROM
    @Results R
INNER JOIN
    UserValues UV
    ON UV.User = R.UserID
WHERE
    UV.Field = 'name'

UPDATE
    R
SET
    R.Email = UV.Value
FROM
    @Results R
INNER JOIN
    UserValues UV
    ON UV.User = R.UserID
WHERE
    UV.Field = 'Email'

UPDATE
    R
SET
    R.Phone = UV.Value
FROM
    @Results R
INNER JOIN
    UserValues UV
    ON UV.User = R.UserID
WHERE
    UV.Field = 'Phone'


SELECT * FROM @Results
1 голос
/ 05 мая 2010

Вы можете использовать самостоятельное соединение:

SELECT User1.User, User1.Value as Name, User2.Value as Email,
  User3.Value as Phone
FROM Users User1
JOIN Users User2
  ON User2.User = User1.User
JOIN Users User3
  ON User3.User = User1.User
WHERE User1.Field = 'name' AND User2.Field = 'email' AND User3.Field = 'phone'
ORDER BY User1.User

Я проверил этот запрос, и он работает.

0 голосов
/ 05 мая 2010

Вариант ответа Хавьера , который имеет мой голос.

SELECT 
  UserName.name, UserEmail.email, UserPhone.phone
FROM 
  Users            AS UserName 
  INNER JOIN Users AS UserEmail ON UserName.User = UserEmail.User
    AND UserName.field = 'name' AND UserEmail.field = 'email'
  INNER JOIN Users AS UserPhone ON UserName.User = UserPhone.User
    AND UserPhone.field = 'phone'

Используйте LEFT JOINs, если не все атрибуты гарантированно существуют. Сложный индекс более (User,Field), вероятно, был бы полезен для этого.

0 голосов
/ 05 мая 2010

В MySQL вы можете сделать что-то вроде этого:

SELECT
    id,
    group_concat(CASE WHEN field='name' THEN value ELSE NULL END) AS name,
    group_concat(CASE WHEN field='phone' THEN value ELSE NULL END) AS phone,
    ...
FROM test
GROUP BY id

Агрегатная функция на самом деле не имеет значения, если у вас есть только одно поле каждого типа. Вы также можете использовать min() или max() вместо того же эффекта.

0 голосов
/ 05 мая 2010

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

select user, name, email, phone from
(select user, value as name from table where field='name')
natural join
(select user, value as email from table where field='email')
natural join
(select user, value as phone from table where field='phone')
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...