Як вирішити неоднозначні імена стовпців під час отримання результатів?


75

У моїй базі даних є дві таблиці:

НОВИНИ таблиця зі стовпцями:

  • id - ідентифікатор новини
  • user - ідентифікатор користувача автора)

Таблиця USERS зі стовпцями:

  • id - ідентифікатор користувача

Я хочу виконати цей SQL:

SELECT * FROM news JOIN users ON news.user = user.id 

Коли я отримую результати в PHP, я хотів би отримати асоціативний масив і отримати імена стовпців за $row['column-name']. Як отримати ідентифікатор новин та ідентифікатор користувача з однаковим іменем стовпця?

Відповіді:


119

Ви можете встановити псевдоніми для вибраних стовпців:

$query = 'SELECT news.id AS newsId, user.id AS userId, [OTHER FIELDS HERE] FROM news JOIN users ON news.user = user.id'

1
Ця відповідь зробила відповіді на stackoverflow.com/questions/9233387/sql-should-i-use-a-join роботою для мене, дякую!
AVProgrammer

3
Основним недоліком цього підходу є те, що на відміну від цього SELECT *, запит тісно пов’язаний зі структурою таблиці. Тобто, кожного разу, коли ви додаєте / видаляєте / перейменовуєте стовпці у схемі, ви повинні відповідно змінювати свій запит.
Рон Інбар,

4
@RonInbar. SELECT *є кошмаром технічного обслуговування у великих системах. Ви припускаєте, що додавання стовпця є простим і не вимагає жодних змін у SQL, якщо використовується SELECT *, проте це не так, як у проблемі псевдонімів, визначеній тут. Це також може надати непотрібний вплив на продуктивність, якщо нове поле велике. Він також може зламати елементи керування на стороні клієнта, які очікують певного числа або полів. Я можу продовжувати, але вигоди від SELECT *зникнення дуже швидко зникають, коли ви розумієте вплив, який він завдає на ремонтопридатність та продуктивність.
simo.3792

1
@ simo.379209 Я б скоріше сказав, що вибрати * МОЖЕ бути кошмаром. Це було в нашому останньому. У нашій поточній системі все побудовано з нуля, щоб використовувати розширювані структури даних. Перевірки мають вигляд "чи є ці дані наявні" немає "чи є ці дані і їх більше немає". Ми збираємось помістити мавпу хаосу, яка рандомізує порядки стовпців і додає нові стовпці. Ми отримали багато користі завдяки спроможності розробляти функції швидше і з меншою кількістю поломок. Чи буде select * поганим, цілком залежить від контексту, навіть у дуже великих системах.
Макс Мерфі

Здається, повинен бути варіант, де якщо ви select table.idповернете стовпець table.id. Здається, це проста функція, яка запобігає згладжуванню. У php це буде трохи важко, якщо вам доведеться псевдонім select.
Каньйон Колоба

42

Ви можете або використовувати числові індекси ( $row[0]), або краще, використовувати ASв MySQL:

SELECT *, user.id AS user_id FROM ...


13
Також зауважте, що якщо ви використовуєте wildwarcd *, він повинен бути першим у пункті select. ПРИМІТКА:SELECT user_id,* ...
Андерс

2
Це правда, але ви завжди можете зробити це, SELECT user.id AS user_id, user.* FROM ...якщо порядок важливий.
Кирилл

23

Я просто зрозумів це. Це, мабуть, погана практика, але в цьому випадку це спрацювало для мене.

Я один з ледачих людей, які не хочуть створювати псевдоніми або виписувати кожну назву стовпця префіксом таблиці.

Ви можете вибрати всі стовпці з певної таблиці, використовуючи table_name.*оператор select.

Коли ви продубльовані імена стовпців, mysql перезапише з першого на останній. Дані з першого продубльованого імені стовпця будуть перезаписані, коли воно знову зустріне це ім'я стовпця. Отже, дублікат назви стовпця, що надходить останньою, виграє.

Якщо я приєднуюсь до 3 таблиць, кожна з яких містить дубльоване ім'я стовпця, порядок таблиць у операторі select визначатиме, які дані я отримую для дублюючого стовпця.

Приклад:

SELECT table1.* , table2.* , table3.* FROM table1 LEFT JOIN table2 ON table1.dup = table2.dup LEFT JOIN table3 ON table2.dup = table3.dup;

У наведеному вище прикладі значення, яке dupя отримую, буде від table3.

Що робити, якщо я хочу dupбути цінністю від table1?

Тоді мені потрібно зробити це:

SELECT table3.* , table2.* , table1.* FROM table1 LEFT JOIN table2 ON table1.dup = table2.dup LEFT JOIN table3 ON table2.dup = table3.dup;

Тепер, table1виходить останнім, тому значенням dupбуде значення з таблиці1.

Я отримав цінність, для якої я хотів, dupне виписуючи кожен окремий дивовижний стовпець, і я все ще отримую всі стовпці для роботи. Ага!

Я знаю, що значення dupмає бути однаковим у всіх 3 таблицях, але що, якщо table3не має відповідного значення для dup? Тоді dupв першому прикладі буде порожнім, і це буде облом.


3
Покладатися на бездокументарне замовлення, яке працює зараз для вас, і не призведе до помилок, якщо зміни в реалізації бази даних є ВИДАЛЬНОЮ ідеєю.
simo.3792

2
Взлом замовлення страшний, але +1 за те, table.*що я можу вибрати, щоб я міг це зробити SELECT table1.*, table2.this_one_column_need FROM table1 INNER JOIN table2 ON....
tobek

10

@ Джейсон. Ви маєте рацію, за винятком того, що винуватцем є php, а не mysql. Якщо ви помістите свій файл JOINу Mysql Workbench, ви отримаєте три стовпці з точно однаковим іменем (по одному для кожної таблиці), але не з однаковими даними (деякі будуть, nullякщо ця таблиця не збігається з JOIN).

У PHP , якщо ви використовуєте MYSQL_NUMв mysql_fetch_array()то ви отримаєте всі стовпці. Проблема полягає у використанні mysql_fetch_array()з MYSQL_ASSOC. Потім всередині цієї функції php будує значення, яке повертається, так:

$row['dup'] = [value from table1]

а пізніше ...

$row['dup'] = [value from table2]

...

$row['dup'] = [value from table3]

Отже, ви отримаєте лише значення з таблиці3. Проблема полягає в тому, що набір результатів з mysql може містити стовпці з однаковим іменем, але асоціативні масиви в php не дозволяють дублювати ключі в масивах. Коли дані зберігаються в асоціативних масивах, у php, частина інформації мовчки втрачається ...


9

Ви можете зробити щось на зразок

SELECT news.id as news_id, user.id as user_id ....

І тоді $row['news_id']буде ідентифікатором новин і $row['user_id']буде ідентифікатором користувача


9

Ще одна порада: якщо ви хочете мати чистіший PHP-код, ви можете створити VIEW у базі даних, наприклад

Наприклад:

CREATE VIEW view_news AS
SELECT
  news.id news_id,
  user.id user_id,
  user.name user_name,
  [ OTHER FIELDS ]
FROM news, users
WHERE news.user_id = user.id;

У PHP:

$sql = "SELECT * FROM view_news";

3

У мене була така сама проблема з динамічними таблицями. (Таблиці, які, як передбачається, мають ідентифікатор, щоб мати змогу приєднатися, але без будь-яких припущень для решти полів.) У цьому випадку ви не знаєте псевдонімів раніше.

У таких випадках спочатку можна отримати імена стовпців таблиці для всіх динамічних таблиць:

$tblFields = array_keys($zendDbInstance->describeTable($tableName));

Де $ zendDbInstance є екземпляром Zend_Db, або ви можете використовувати одну з функцій тут, щоб не покладатися на Zend php pdo: отримайте ім'я стовпців таблиці

Тоді для всіх динамічних таблиць ви можете отримати псевдоніми та використовувати $ tableName. * Для тих, які вам не потрібні псевдоніми:

$aliases = "";
foreach($tblKeys as $field)
    $aliases .= $tableName . '.' . $field . ' AS ' . $tableName . '_' . $field . ',' ;
$aliases = trim($aliases, ',');

Ви можете обернути весь цей процес в одну загальну функцію і просто мати чистіший код або, якщо хочете, стати більш ледачим :)


0

Якщо вам не хочеться створювати псевдоніми, ви можете просто поставити префікс імен таблиць.

Таким чином ви можете краще автоматизувати генерацію своїх запитів. Крім того, найкраще не використовувати select * (це, очевидно, повільніше, ніж просто вибір полів, які вам потрібні. Крім того, лише чітко називайте поля, які ви хочете мати.

SELECT
    news.id, news.title, news.author, news.posted, 
    users.id, users.name, users.registered 
FROM 
    news 
LEFT JOIN 
    users 
ON 
    news.user = user.id

З цим є проблема. PHP видалить префікс. так що якщо ви отримаєте результат, то не зможете зробити $row['new.id'].. будь-яке рішення для цього?
CoffeDeveloper

0

Ось відповідь на вищезазначене - і простий, і також працює з результатами JSON, які повертаються. Хоча запит SQL автоматично додає префікс імен таблиць до кожного екземпляра однакових імен полів, коли ви використовуєте SELECT *, кодування JSON результату для відправки назад на веб-сторінку, ігнорує значення цих полів з дублікатом імені і замість цього повертає значення NULL .

Саме те, що він робить, включає перший екземпляр дубльованого імені поля, але робить його значення НУЛЕМ. А другий екземпляр імені поля (в іншій таблиці) повністю опущений, як ім’я поля, так і значення. Але коли ви перевіряєте запит безпосередньо в базі даних (наприклад, за допомогою Navicat), усі поля повертаються у набір результатів. Лише при наступному кодуванні JSON цього результату вони мають значення NULL, а наступні повторювані імена повністю опускаються.

Отже, найпростіший спосіб вирішити цю проблему - спочатку зробити SELECT *, а потім виконати псевдоніми поля для дублікатів. Ось приклад, коли обидві таблиці мають однакові поля з іменем сайту.

SELECT *, w.site_name AS wo_site_name FROM ws_work_orders w JOIN ws_inspections i WHERE w.hma_num NOT IN(SELECT hma_number FROM ws_inspections) ORDER BY CAST(w.hma_num AS UNSIGNED);

Тепер у декодованому JSON ви можете використовувати поле wo_site_name і воно має значення. У цьому випадку назви сайтів мають спеціальні символи, такі як апострофи та одинарні лапки, отже, кодування при початковому збереженні та декодування при використанні результату з бази даних.

...decHTMLifEnc(decodeURIComponent( jsArrInspections[x]["wo_site_name"]))

Ви завжди повинні ставити * першим в операторі SELECT, але після нього ви можете включити скільки завгодно іменованих та псевдонімових стовпців, оскільки неодноразовий вибір стовпця не викликає проблем.


0

Використовуючи PDO для взаємодії з базою даних, можна використовувати режим отримання PDO :: FETCH_NAMED, який може допомогти вирішити проблему:

$sql = "SELECT * FROM news JOIN users ON news.user = user.id";
$data = $pdo->query($sql)->fetchAll(PDO::FETCH_NAMED);
foreach ($data as $row) {
    echo $row['column-name'][0]; // from the news table
    echo $row['column-name'][1]; // from the users table
    echo $row['other-column']; // any unique column name
}

-1

Існує два підходи:

  1. Використання псевдонімів; у цьому методі ви даєте нові унікальні імена (ALIAS) різним стовпцям, а потім використовуєте їх у пошуку PHP. напр

    SELECT student_id AS FEES_LINK, student_class AS CLASS_LINK 
    FROM students_fee_tbl 
    LEFT JOIN student_class_tbl ON students_fee_tbl.student_id = student_class_tbl.student_id
    

    а потім отримати результати в PHP:

    $query = $PDO_stmt->fetchAll();
    
    foreach($query as $q) {
        echo $q['FEES_LINK'];
    }
    
  2. Використання позиції місця або індексу стовпця набору результатів; у цьому позиції масиву використовуються для посилання на дубльовані імена стовпців. Оскільки вони відображаються в різних позиціях, індексні номери, які будуть використовуватися, завжди є унікальними. Однак цифри позиціонування індексу починаються з 0. наприклад

    SELECT student_id, student_class 
    FROM students_fee_tbl 
    LEFT JOIN student_class_tbl ON students_fee_tbl.student_id = student_class_tbl.student_id
    

    а потім отримати результати в PHP:

    $query = $PDO_stmt->fetchAll();
    
    foreach($query as $q) {
        echo $q[0];
    }
    
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.