MySQL View: соединять таблицы, не вызывая дублирования данных в каждой строке?

У меня в основном та же проблема, что и здесь:

Представление SQL: соединение таблиц без что приводит к дублированию данных в каждой строке?

За исключением этого вопроса, он использовал SQL, а я использую mysql. Мне интересно, возможен ли такой же запрос в mysql. Если да, то может у меня неправильный синтаксис?

Я пытаюсь сделать что-то вроде

select a.name as account_Name, 
   p.description as property_DESCRIPTION, 
   p.address as property_ADDRESS, 
   null as vehicles_DESCRIPTION,
   null as vehicles_MAKE, 
   null as vehicles_MODEL
from Accounts a
    inner join Properties p
        on a.accountid = p.accountid
UNION ALL   
select a.name as account_Name, 
   null as property_DESCRIPTION, 
   null as property_ADDRESS, 
   v.description as vehicles_DESCRIPTION,
   v.make as vehicles_MAKE, 
   v.model as vehicles_MODEL
from Accounts a
    inner join vehicles v
        on a.accountid = v.accountid

Вот мой фактический код:

SELECT user.first_name, user.last_name, upi.image_id, NULL AS friends.friend_user_id FROM user 
INNER JOIN user_profile_images as upi ON (user.user_id = upi.user_id) 
UNION 
SELECT user.first_name, user.last_name, NULL AS upi.image_id, friends.friend_user_id FROM user 
INNER JOIN friends ON (user.user_id = friends.user_id) 
WHERE user.user_id = '$profile_id'

где у меня есть 3 таблицы: пользователь, user_profile_images и друзья. И user_profile_images, и друзья связаны с пользователем через user_id. Таким образом, у пользователя может быть несколько изображений профиля, а также несколько записей друзей. Я могу выложить таблицы диаграмм, если это не имеет смысла. Но то, что я хочу, - это в основном представление всей информации с полями NULL, если они не относятся к общему представлению.

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


person aeroshock    schedule 12.03.2012    source источник
comment
SQL — это язык, частной реализацией которого является MySQL. Нет ничего, кроме SQL. КОГДА вы делаете union all, вы заставляете mysql возвращать все строки из объединенных запросов. удалите ключевое слово all, и mysql удалит для вас повторяющиеся строки.   -  person Marc B    schedule 12.03.2012
comment
Также прочитайте о левых/правых соединениях, так как иногда они могут быть лучшим решением.   -  person Shomz    schedule 12.03.2012
comment
Ваш фактический код, вероятно, выдаст ошибку, потому что у вас не одинаковое количество столбцов в каждой таблице - во втором SELECT есть upi.image_id, но не в первом.   -  person mathematical.coffee    schedule 12.03.2012
comment
Моя ошибка, я скопировал плохой кусок кода, снова обновил код, который я до сих пор не могу заставить работать...   -  person aeroshock    schedule 12.03.2012
comment
Ваше сообщение об ошибке явно связано с PHP (mysqli_num_rows() ожидает параметр 1...) - можете ли вы либо выполнять запросы из приглашения MySQL напрямую (чтобы мы могли разобраться с проблемами MySQL, прежде чем переходить к проблемам PHP), или, по крайней мере, опубликуйте код PHP, который вы используете для запроса базы данных, включая строку, из которой исходит ошибка. Кроме того, в строке, которую вы используете для выполнения запроса, не могли бы вы сделать $con->query('...') or die($con->error) (или mysqli_query(...) or die(mysqli_error(...))), чтобы мы могли понять, в чем ошибка — в MySQL или в PHP, и в чем именно ошибка. .   -  person mathematical.coffee    schedule 12.03.2012
comment
Конечно, прямой запрос дает: У вас есть ошибка в синтаксисе SQL; проверьте руководство, соответствующее версии вашего сервера MySQL, для правильного синтаксиса для использования рядом с «.friend_user_id FROM user INNER JOIN user_profile_images as upi ON (user.user_i» в строке 1   -  person aeroshock    schedule 12.03.2012
comment
Похоже, вам не хватает подчеркивания между friend и user_id ? то есть friend user_id -> friend_user_id? Кроме того, вам на самом деле не нужны эти имена таблиц в псевдонимах столбцов — подойдут NULL AS image_id и NULL AS friend_user_id.   -  person mathematical.coffee    schedule 12.03.2012


Ответы (2)


Решение, как предлагает @MarcB, состоит в том, чтобы использовать UNION, а не UNION ALL.

Однако у меня к вам вопрос - зачем вообще использовать UNION? Следующее эквивалентно, за исключением того, что если (скажем) аккаунт 1 имеет одно имущество и одно транспортное средство, вместо получения:

account_Name   property_DESCRIPTION   vehicles_MAKE
  account1          property1             NULL
  account1           NULL                vehicle1

Ты получишь

account_Name   property_DESCRIPTION   vehicles_MAKE
  account1          property1            vehicle1

Запрос:

SELECT a.name as account_Name, 
   p.description as property_DESCRIPTION, 
   p.address as property_ADDRESS, 
   v.description as vehicles_DESCRIPTION,
   v.make as vehicles_MAKE, 
   v.model as vehicles_MODEL
FROM Accounts a
LEFT JOIN Properties p
        on a.accountid = p.accountid
LEFT JOIN vehicles v
        on a.accountid = v.accountid
WHERE p.description IS NOT NULL AND v.make IS NOT NULL

Примечание. Последняя строка (НЕ НУЛЕВАЯ как для p, так и для v) имитирует часть «таблицы учетных записей» INNER JOIN и гарантирует, что отображаются только учетные записи, имеющие по крайней мере свойство ИЛИ транспортное средство. Замените столбцы id на p и v там.

person mathematical.coffee    schedule 12.03.2012
comment
Я не слишком разбираюсь в SQL, поэтому не уверен в других способах сделать это, просто UNION имеет смысл для вопроса по другой ссылке. Я попытался удалить ALL из UNION, но запрос по-прежнему не работает. Я пробовал упомянутые вами левые соединения, но все равно получаю повторяющиеся строки. Я обновил свой вопрос, чтобы было более понятно, что происходит. - person aeroshock; 12.03.2012
comment
Что вы подразумеваете под тем, что все еще не работает - вы получаете дубликаты или получаете ошибку (и какую ошибку)? Повторная попытка левых соединений и получение повторяющихся строк - вы имеете в виду, что есть некоторые строки, в которых все столбцы идентичны? Отличаются ли они каким-либо полем, которое вы не уточнили SELECT? (Я могу сделать так, чтобы симптомы исчезли, но, вероятно, под ней что-то больное, поэтому я пытаюсь понять, что именно). Можете ли вы привести пример повторяющихся строк? ваше здоровье. - person mathematical.coffee; 12.03.2012
comment
Под не работает я имею в виду, что получаю сообщение об ошибке: mysqli_num_rows() ожидает, что параметр 1 будет mysqli_result, заданным логическим значением. Что заставляет меня думать, что синтаксис как-то неверен. Извини, я плохой в е - person aeroshock; 12.03.2012
comment
Под не работает я имею в виду, что получаю сообщение об ошибке: mysqli_num_rows() ожидает, что параметр 1 будет mysqli_result, заданным логическим значением. Что заставляет меня думать, что синтаксис как-то неверен. Извините, я плохо объясняю, а не точные дубликаты. Например: будет несколько строк с одинаковыми данными в столбцах user и user_image_profile, но разными данными в столбцах друзей. Итак, я хочу, чтобы каждая уникальная строка данных из таблицы имела свою собственную строку, а столбцы других таблиц были нулевыми. Что, как я думаю, может быть неэффективным, и я поступаю неправильно...? - person aeroshock; 12.03.2012
comment
Делая это таким образом, я понял, что таблица будет умножаться на количество строк в user_profile_images x количество строк в столбце друзей. Итак, если у пользователя есть 4 друга и 7 изображений, он вернет 28 строк, а если он добавит еще одно изображение, то получится 8, 32 строки. Кажется, это не самый эффективный способ выполнить этот запрос... не лучше ли использовать 2 отдельных запроса для таблиц friends и user_profile_images? Извините, что сменил тему, но не хочу отнимать у вас слишком много времени! Просто пытаюсь найти наиболее эффективный способ ведения дел. - person aeroshock; 12.03.2012
comment
Вы делаете это неправильно - выполняя NULL, вы получите гораздо больше строк, чем при использовании соединения и их объединении. Конечно, у вас будут дубликаты user/user_image_profile с разными друзьями, потому что у пользователя один профиль, но много друзей. Запрашивая совокупный результат, будь то JOIN или UNION, у вас всегда будет эта проблема. Если вас это беспокоит, сделайте два отдельных запроса на выборку (вы все равно хотите, чтобы они были объединены? Вы могли бы сделать два отдельных выборки и отсортировать по идентификатору пользователя на обоих?) - person mathematical.coffee; 12.03.2012
comment
Сделайте два отдельных запроса, если по какой-то причине они вам не нужны. - person mathematical.coffee; 12.03.2012

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

select a.name as account_Name, 
p.description as property_DESCRIPTION,
p.address as property_ADDRESS,
v.description as vehicles_DESCRIPTION,
v.make as vehicles_MAKE, 
v.model as vehicles_MODEL
from accounts a
left outer join properties p on p.accountid = a.accountid
left outer join vehicles v on v.accountid = a.accountid;

Вот как было проверено решение. Сначала я создал три таблицы.

CREATE TABLE `accounts` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

CREATE TABLE `properties` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`accountid` int(11) NOT NULL,
`description` varchar(255) COLLATE utf8_bin NOT NULL,
`address` varchar(255) COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

CREATE TABLE `vehicles` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`accountid` int(11) NOT NULL,
`description` varchar(255) COLLATE utf8_bin NOT NULL,
`make` varchar(100) COLLATE utf8_bin NOT NULL,
`model` varchar(100) COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

Затем я вставил данные в каждую таблицу.

INSERT INTO `demo`.`accounts` (
`accountid` ,
`name`
)
VALUES (
NULL , 'techport80.com'
);

INSERT INTO `demo`.`properties` (
`id` ,
`accountid` ,
`description` ,
`address`
)
VALUES (
NULL , '1', 'office', '123 may street');

INSERT INTO `demo`.`vehicles` (
`id` ,
`accountid` ,
`description` ,
`make` ,
`model`
)
VALUES (
NULL , '1', 'motorcycle', 'honda', 'shadow'
);

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

INSERT INTO `demo`.`vehicles` (
`id` ,
`accountid` ,
`description` ,
`make` ,
`model`
)
VALUES (
NULL , '1', 'passenger car', 'Ford', 'Mustang'
);

Теперь, если я проверю свое решение, я увижу 2 строки, по одной для каждого автомобиля. Но две строки не совсем дублируются. Хотя есть несколько столбцов с такими же данными. Прежде всего, информация об учетной записи.

INSERT INTO `demo`.`properties` (
`id` ,
`accountid` ,
`description` ,
`address`
)
VALUES (
NULL , '1', 'home', '321 yam street'
);

Затем я добавил второй адрес. Теперь возвращаются четыре строки. По одному на каждое транспортное средство и по одному на каждый адрес. Но тем не менее ни одна из строк не повторяется.

Наконец я добавил еще один автомобиль. Это должно было вызвать дисбаланс между транспортным средством и имуществом.

INSERT INTO `demo`.`vehicles` (
`id` ,
`accountid` ,
`description` ,
`make` ,
`model`
)
VALUES (
NULL , '1', 'Van', 'Chev', 'Cargo'
);

Теперь у нас 6 рядов. По одному на каждое транспортное средство, и каждое транспортное средство также связано с двумя свойствами. (Так 2 х 3)

Выполнив это упражнение, я задаюсь вопросом, будет ли иерархическое представление данных лучшей моделью для этих данных. Возможно, для представления данных можно использовать XML или JSON. Для этой задачи вы можете использовать хранимую функцию, но лично я бы сначала рассмотрел язык программирования, такой как PHP, C#, C++ или множество других.

ХТН

person Ronald Weidner    schedule 12.03.2012
comment
Спасибо, я попробовал левые соединения, как вы сказали, и все равно получил повторяющиеся строки. - person aeroshock; 12.03.2012