Держи позиции сайтов под прицелом (Yandex XML)

Все, кто занимается продвижением или делает это не специально, интересуется позициями своего сайта/сайтов. Хоть и каждый для этого дела использует разные программы/скрипты, я предлагаю же создать что-то под себя почти с нуля. Сегодня мы будем снимать позиции с популярной в России поисковой системы Яндекс. Яндекс разработчикам предлагает очень удобный интерфейс для взаимодействия. Благодаря которому мы с легкостью можем встроить на свой сайт поиск или что-нибудь полезное еще. Но сегодня мы ограничимся лишь снятием для конкретного сайта и ключевого слова позиции. В начале нам зарегистрировать свой IP-адрес, потому как Яндекс накладывает ограничение на количество запросов с одного IP-адреса, но их с лихвой хватит для нескольких сайтов. Нетерпеливым предлагаю сразу скачать исходник со всеми скриптами и картинкой, отображающейся когда идет запрос, но еще не получен результат. И тут применяется AJAX технология, реализовать которую нам помог так любимый мной jQuery.

Для начала сам файл, который и будет принимать наши запросы с позицией и сайтом, формировать XML и отправлять Яндексу.

Файл post.php

<?php
header ("Content-Type: text/html;charset=utf-8");
$host = isset($_REQUEST['host']) ? $_REQUEST['host'] : '';
$query = isset($_REQUEST['query']) ? $_REQUEST['query'] : '';

$query_esc = htmlspecialchars($query);
$host_esc  = htmlspecialchars($host);

$host = preg_replace("[^http://|www\.]", '', $host);

$page  = 0;
$found = 0;
$pages = 50;
$error = false;
$exit = 0;
while (!$exit && $page < $pages && $host)
{
   // XML запрос
$doc = <<<DOC
   <?xml version='1.0' encoding='utf-8'?>
      <request>
      <query>$query_esc</query>
      <page>$page</page>
      <maxpassages>0</maxpassages>
      <groupings>
         <groupby attr='d' mode='deep' groups-on-page='10' docs-in-group='1' curcateg='-1'/>
      </groupings>
   </request>
DOC;

    $context = stream_context_create(array(
        'http' => array(
      'method'=>"POST",
      'header'=>"Content-type: application/xml\r\n" .
           "Content-length: " . strlen($doc),
      'content'=>$doc
        )
    ));
    $response = file_get_contents('http://xmlsearch.yandex.ru/xmlsearch', true, $context);

    if ( $response ) {
       $xmldoc = new SimpleXMLElement($response);
       $xmlresponce = $xmldoc->response;
        if ($xmlresponce->error) {
            print "Возникла следующая ошибка: " . $xmlresponce->error . "<br/>\n";
            $exit  = 1;
            $error = true;
            break;
        }
        $pos = 1;
        $nodes = $xmldoc->xpath('/yandexsearch/response/results/grouping/group/doc/url');
        foreach ($nodes as $node) {
            if ( preg_match("/^http:\/\/(www\.)?$host/i", $node) ) {
                $found = $pos + $page * 10;
                $exit = 1;
                break;
            }
            $pos++;
        }
        $page++;
    } else {
        print "внутренняя ошибка сервера\n";
        $exit = 1;
    }
}
if (!$error) {
    // если что-то найдено, то выводим результат 
    if ($found) {
      print $found;
    } elseif ($host) {
        print "<p>сайт находится далее, чем на ". $pages*10 ." месте в выдаче";
    }
}
?>

В строке указывается количество страниц, которое нужно просмотреть в поисках своего сайта.

И теперь HTML-файл, который обернет нам это большой, но несложный код.

index.php
<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
      <script type="text/javascript" src="js/jquery.js"></script>
      <script type="text/javascript">
         $('document').ready(function() {
            $('img.busy').hide();
            
            $("button#push").click(function() {
               $('div#output div').text('');
               var host = $('input#host').val();
               var query = $('input#query').val();
               var queryString = '/post.php?query=' + query + "&host=" + host;
               $('img.busy').bind("ajaxSend", function() {
                  $(this).show();
               }).bind("ajaxComplete", function() {
                  $(this).hide();
               });

               $.get(queryString, function(data) {
                  $('div#output div').text(data);
               });
            });
         });
      </script>
   </head>
   <body>
      <table width="40%" border="0" align="center" cellpadding="3" cellspacing="3">
        <tr>
         <td>хост</td>
         <td><input id="host" type="text"></td>
         <td><center><div id="output"><img src="img/ajax.gif" class="busy"/><div></div></div></center></td>
        </tr>
        <tr>
         <td>запрос</td>
         <td><input id="query" type="text"></td>
         <td><center><button id="push">запросить</button></center></td>
        </tr>
      </table>    
   </body>
</html>

Вот и все, основную часть кода предоставил Яндекс, я по большей части дописал лишь оболочку и немного модернизировал скрипт запросов.

Этот скрипт легко можно переделать так, чтобы каждый день он запускался по расписанию и проверял позиции для некоторых ключевых слов для десятка сайтов, сохранял полученную информацию в БД. Насколько мне известно, некоторые SEO-шники получают деньги лишь имея продвигаемый сайт в десятке. Желаю, чтобы позиции ваших сайтов только росли! =)

Добавлено: 10 Мая 2018 07:30:02 Добавил: Андрей Ковальчук

Файл Sitemap.xml

Часто в "robots.txt" мы видим запись

Sitemap: http://sait.ru/Sitemap.xml.


Что же это такое? Sitemap - в переводе на русский язык Карта сайта... Это довольно таки нужный файл. Нужен он для роботов, которые индексируют наш сайт. В файле "robots.txt" прописывается эта строка и Робот уже знает, что может зайти в файл "Sitemap.xml" и найти ВСЕ ссылки, то есть ВСЕ страницы сайта. В этом случае индексация сайта роботами улучшается. Если сайт динамический и в нем постоянно добавляются странички, то файл "Sitemap.xml" просто обязателен. Итак,
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>http://f9r.ru/</loc>
    <priority>1</priority>
    <changefreq>hourly</changefreq>
  </url>
  <url>
    <loc>http://f9r.ru/index.html</loc>
    <priority>0.5</priority>
    <changefreq>weekly</changefreq>
  </url>
  <url>
    <loc>http://f9r.ru/all.php?act=forum&amp;how=web</loc>
    <priority>0.1</priority>
    <changefreq>monthly</changefreq>
  </url>
</urlset>


Вы видите листинг стандартного файла "Sitemap.xml", в нем прописаны ТРИ ссылки с нашего сайта:

http://f9r.ru/
http://f9r.ru/index.html
http://f9r.ru/all.php?act=forum&how=web

Причем, как Вы можете видеть символ "&" в файле Sitemap.xml заменен на "&amp;" - это одно из самых главных условий, которое надо неукоснительно выполнять.

Кроме этого, давайте разберем устройство сайта "Sitemap.xml", Вы видите, что он представляет собой открывающиеся и закрывающиеся теги, между которыми, как и в HTML располагается информация.

Итак, файл находится между тегами

<urlset>
Здесь основная инфа
</urlset> 


Далее идут теги, в которых располагаютя URLы

<url>
Здесь инфа об URLах
</url>


Далее:

    <loc>ИМЯ URLа</loc>
    <priority>Приоритет URLа</priority>
    <changefreq>Как часто сканировать URL</changefreq>


Вы видите, что сложного в этом ничего нет, Приоретет может быть от 0.1 до 1, в зависимости от значимости для вашего сайта данной страницы, Вы можете попросить робота сканировать данную страницу:

hourly - каждый час
weekly - раз в неделю
monthly - раз в месяц
Теперь, когда мы разобрались как устроен "Sitemap.xml" надо сказать о том, что еще совсем не значит, что роботы будут неукоснительно исполнять то, что прописано в данном файле, но тем не менее, практика показала, с файлом "Sitemap.xml" сканирование роботами происходит лучше.

Итак, перейдем к главному. Можно просто прописать файл в головную папку сайта и записать туда все Urlы вашего сайта, использую ту спецификацию, что здесь приведена. Если страниц на сайте немного, то труда это не составит. Но, если страниц больше 1000, то вы замучаетесь прописывать вручную все страницы вашего сайта в "Sitemap.xml". Однако, так же можно воспользоваться сервисами, которые сканируют ваш сайт и выдают Вам распечатку "Sitemap.xml", то же не очень хорошее решение, так как такие сервисы более 1000 страниц не выводят.

И самое главный минус в том, чтобы просто прописать файл Сайтемап в головную папку - это то, что ваш сайт может начать сканировать программа - робот с целью украсть его и сделать зеркально похожий сайт. В этом случае, такой программе даже не надо будет искать все Ваши ссылки, так как один раз скачав ваш "Sitemap.xml", такой программе Вы сами облегчете труды, она все ваши Urlы сразу узнает.

В итоге, приходим к выводы, что файл Сайтемап нельзя прописывать в головной папке, как же быть, чтобы нормальным роботам можно было его отдать, а всем остальным нет?

Решение напрашивается само собой, создавать его динамически, смотреть какой робот пытается его забрать, отдавать только хорошим роботам. На данный момент на нашем сайте мы отдаем этот файл только: Googlebot, Yandex, Rambler, Yahoo, Msnbot, Aport, Mail.Ru. Вы видите здесь только 7 роботов.

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

RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} ˆ/Sitemap\.xml$ [NC]
RewriteRule ˆ.* /sitemap.php?type=xml [L]



Вы видите, что при запросе "http://f9r.ru/Sitemap.xml", скрыто без ридеректа мы отдадим страничку "http://f9r.ru/sitemap.php?type=xml", то есть отдаем файл sitemap.php, и дополнительно прописываем, что формат файла type=xml

Примерный листинг файла sitemap.php:

<?php 
    # Определим $_User_host, подробнее о функии ip() здесь... 
$_User_ip = ip(); 
$_User_host = @gethostbyaddr ($_User_ip); // узнаем host робота 

    # $_BName и $_Brobot определим, подробнее о функции здесь... 
$_ARR = detect_browser ($_User_agent); 
$_BName = $_ARR[0]; // если робот, то вывод будет $_BName = Robot 
$_Brobot = $_ARR[3]; // имя робота 

    # если зашел робот 
if ( $_BName == 'Robot' ){ 
    # Плохой броузер, который представляется Google Yandex Yahoo Rambler Msnbot Aport Mail.Ru 
     if ( $_User_ip != $_User_host and 
         ( $_Brobot == 'Googlebot' or 
         $_Brobot == 'Yandex' or 
         $_Brobot == 'Rambler' or 
         $_Brobot == 'Yahoo' or 
         $_Brobot == 'Msnbot' or 
         $_Brobot == 'Aport' or 
         $_Brobot == 'Mail.Ru') 
         ){ 

    # проверяем не врет ли нам робот, сверяя его имя с имененм хоста 
         if ( ( $_Brobot == 'Googlebot' and ! preg_match ('~googlebot\.com~', $_User_host) ) or 
         ( $_Brobot == 'Yandex' and ! preg_match ('~yandex(\.ru|\.net)~', $_User_host) ) or 
         ( $_Brobot == 'Rambler' and ! preg_match ('~rambler~', $_User_host) ) or 
         ( $_Brobot == 'Yahoo' and ! preg_match ('~yahoo\.net~', $_User_host) ) or 
         ( $_Brobot == 'Aport' and ! preg_match ('~wmd\.rol\.ru~', $_User_host) ) or 
         ( $_Brobot == 'Mail.Ru' and ! preg_match ('~mail\.ru~', $_User_host) ) or 
         ( $_Brobot == 'Msnbot' and ! preg_match ('~search\.(live|msn)\.com~', $_User_host) ) 
         ) 

    # если врет выводим error 401 
         { print 'error 401'; } 

    # если НЕ врет выводим sitemap.php 
     else { 

    # печатаем начало "Sitemap.xml" 
     $content = '<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
';


    # считываем файл 'data/site_map.dat' - в этом файле построчно лежат 
    # все наши Urlы плюс через "::" их значимость от 1 до 10 (приоритет) 
     $general = file ( 'data/site_map.dat' );
     $lines = count ($general);

    # прогоняем все это в цикле 
     for ($i=0; $i<$lines; $i++){
         list ($url, $level) = explode ("::",$general[$i]);
    # чему равен приоретет 
         if ($level == 0) { $changefreq = 'hourly'; }
         elseif ($level < 3) { $changefreq = 'weekly'; }
         else { $changefreq = 'monthly'; }
         if ($url=='') { $priority = 1; }
         else { $priority = 1 - $level/10; }

    # выводим url, заменяя в нем "&" на "&amp;" 
         $url = str_replace ('&', '&amp;', $url);

    # добавляем к $content 
        $content .= " <url>
        <loc>http://f9r.ru/".$url."</loc>
        <priority>".$priority."</priority>
        <changefreq>".$changefreq."</changefreq>
        </url>\n";
        }

    # когда цикл закончился, добавляем к $content конечный тег 
         $content .= "</urlset>\n";

    # печатем $content 
         echo $content;
         } 
     } 
}
    # если файл пытается скачать броузер отдаем ему ошибку 
else { print 'error 410'; } 
?>

Вы видите, что проблема решена... Остается вопрос каким образом все ваши странички попадут в файл - базу 'data/site_map.dat', наша программа ЭТО делает сама. Написать скрипт, который подошел бы для всех невозможно, потому что алгоритм хранения данных разный на разных сайтах. Тем не менее, скачайте скрипт нашего сайта и вы сможете увидеть как работает данный механизм, там все это реализовано и все автоматизировано. Кроме этого, можете попробовать в командной строке своего броузера набрать http://f9r.ru/Sitemap.xml, чтобы увидеть результат работы скрипта.

Добавлено: 27 Апреля 2018 19:06:35 Добавил: Андрей Ковальчук

RSS-парсер на PHP

RSS (really simple syndication) был разработан фирмой netscape и представляет собой расширение xml созданное специально для оформления новостных лент.
На сегодняшний день формат пережил уже 2-ю редакцию и является общепринятым стандартом для разметки новостей.

Вот пример простого rss документа:

<?xml version="1.0" encoding="windows-1251"?>
<!doctype rss public "-//netscape communications//dtd rss 0.91//en"
"http://my.netscape.com/publish/formats/rss-0.91.dtd
<rss version="0.91">
<channel>
<title>none</title>
<description>заполнение пустоты</description>
<link>http://riscom.com/~none</link>
</channel>

<image>
<title>none</title>
<url>http://riscom.com/~none/favicon.gif</url>
<link>http://riscom.com/~none</link>
<width>16</width>
<height>16</height>
</image>

<item>
<title>rss - вкусные новости </title>
<link>http://riscom.com/~none/</link>
<description>Что такое rss и с чем его едят</description>
</item>

</rss>


Структура достаточно наглядна и понятна.

Два общих блока (channel и image), применяемых к целому документу и блок(и) item, содержащий сами новости.

Блок channel определяет источник новостей:

title - имя сайта;
description - описание;
link - адрес
Блок image - графическое отображение сайта:

title - название;
link - адрес картинки;
width, height - ширина и высота.
Блоков item может быть сколько угодно и в них описываются сами новости:

title - заголовок;
link - адрес самой новости;
description - описание.
Всё, что находится выше тэга rss называется заголовком документа и применяется к любому xml документу, конечно же с соответствующими корректировками.

Теперь, после того, как мы научились создавать rss документ, давайте подумаем, что нам со всем этим добром делать.

Первая, и самая лёгкая идея, это, конечно же, ничего с ним не делать, просто создать процедурину автоматической генерации rss из любой публикуемой новости и забыть о нём. Дескать, пускай те, кому это нужно, сами парсят его.
Но, предположим, что у нас есть некоторый ресурс, на котором мы хотим публиковать новости всем известного сайта www.ionpopescu.md, а господин popescu ни в какую не хочет предоставлять их (новости), в каком либо другом формате кроме как в rss.
Что нам остаётся?
Правильно! Будем парсить.
Здесь, опять же, есть два выхода: первый - воспользоваться всеми известными парсерами xml типа sablotron и не морочить себе голову, второй - морочить.
У второго варианта есть и ещё одно оправдание, представьте что вы пользуетесь каким-нибудь бесплатным хостингом, и хостер ну ни в какую не хочет устанавливать у себя sablotron или его аналоги.

----------------------------------------------------
И сам скрипт:
<?php

function startelement($parser, $name, $attrs) {
global $tag, $rss;
if ($name == 'rss')
$rss = '^rss';
elseif ($name == 'rdf:rdf')
$rss = '^rdf:rdf';

$tag .= '^' . $name;
}

function endelement($parser, $name) {
global $tag;
global $itemcount, $items;
if ($name == 'item') {
$itemcount++;
if (!isset($items[$itemcount])) $items[$itemcount] = array('title' => '', 'link' => '', 'desc' => '', 'pubdate' => '');
}

$tag = substr($tag, 0, strrpos($tag, '^'));
}

function characterdata($parser, $data) {
global $tag, $chantitle, $chanlink, $chandesc, $rss, $imgtitle, $imglink, $imgurl;
global $items, $itemcount;

$rsschannel = '';
if ($data) {
if ($tag == $rss . '^channel^title') {
$chantitle .= $data;
} elseif ($tag == $rss . '^channel^link') {
$chanlink .= $data;
} elseif ($tag == $rss . '^channel^description') {
$chandesc .= $data;
}
if ($rss == '^rss') $rsschannel = '^channel';

if ($tag == $rss . $rsschannel . '^item^title') {
$items[$itemcount]['title'] .= $data;
} elseif ($tag == $rss . $rsschannel . '^item^link') {
$items[$itemcount]['link'] .= $data;
} elseif ($tag == $rss . $rsschannel . '^item^description') {
$items[$itemcount]['desc'] .= $data;
} elseif ($tag == $rss . $rsschannel . '^item^pubdate') {
$items[$itemcount]['pubdate'] .= $data;
} elseif ($tag == $rss . $rsschannel . '^image^title') {
$imgtitle .= $data;
} elseif ($tag == $rss . $rsschannel . '^image^link') {
$imglink .= $data;
} elseif ($tag == $rss . $rsschannel . '^image^url') {
$imgurl .= $data;
}
}

}

function parserss($url) {
global $tag, $chantitle, $chanlink, $chandesc, $rss, $items, $itemcount, $imgtitle, $imglink, $imgurl;
$chantitle = '';
$chanlink = '';
$chandesc = '';
$imgtitle = '';
$imglink = '';
$imgurl = '';
$tag = '';
$rss = '';

global $items, $itemcount;

$itemcount = 0;
$items = array(0 => array('title' => '', 'link' => '', 'desc' => '', 'pubdate' => ''));

$xml_parser = xml_parser_create();
xml_set_element_handler($xml_parser, "startelement", "endelement");
xml_set_character_data_handler($xml_parser, "characterdata");

@$fp = fopen($url, "r");
$data = "";
while (true) {
@$datas = fread($fp, 4096);
if (strlen($datas) == 0) {
break;
}
$data .= $datas;
}

@fclose($fp);

if ($data != '') {
$xmlresult = xml_parse($xml_parser, $data);
$xmlerror = xml_error_string(xml_get_error_code($xml_parser));
$xmlcrtline = xml_get_current_line_number($xml_parser);

if ($xmlresult)
displaydata();
else
print("error parsing this feed !<br />error: $xmlerror , at line: $xmlcrtline");
} else {
print("error while retriving feed $url");
}

xml_parser_free($xml_parser);
}

function displaydata() {
global $chantitle, $chanlink, $chandesc, $rss, $items, $itemcount, $imgtitle, $imglink, $imgurl;
global $items, $itemcount;
?>
<html><head><title><?= $chantitle ?></title></head>
<body>
<div>
<a href="<?= $chanlink ?>"><img src="<?= $imgurl ?>" alt="<?= $imgtitle ?>" border="0" /></a>
<h1><?= $chantitle ?></h1>
<h3><?= $chandesc ?></h3>
</div>
<hr />
<?php
for($i = 0;$i < count($items)-1;$i++) {
echo "<h4>".$items[$i]['title']."</h4>";
echo "<h5>".$items[$i]['pubdate']."</h5>";
echo "<a href='".$items[$i]['link']."'>".$items[$i]['desc']."</a>";
}
?>
</body></html>

<?php }

$url="http://xmlhack.ru/index.rdf";
parserss($url);
?>

Добавлено: 04 Апреля 2018 15:55:39 Добавил: Андрей Ковальчук

Народная самодеятельность — связи таблиц в MySQL

Первоначально я предполагал сделать выпуск "Народной самодеятельности" со сборником ошибок, допущеных в основном мною :). Кое-что я собирал из статей о традиционных ошибках и т.п. Но жизнь оказывается куда интересней! Пишу про очередную самодеятельность прямо по горячим следам.

Итак, некоторый человек, назовем его Вася, спрашивает Общественность форума про MySQL.

В.: Не совсем понятно как создать связь между таблицами, чтобы в поле первой автоматически вставлялись данные второй (есно выборочно и есно по ИД).

Варивант типа SELECT db.user, db.delete_priv, user.user, user.delete_priv FROM db,user WHERE db.user = user.user не совсем подходит, так как связью это можно назвать с большой натяжкой...

O.: Честно говоря, не понимаю, почему эта связь тебе не подходит? Чем эта связь натянута?

В.: Ну не нравится мне связь, основанная на синтаксисе запросов... хотелось бы чего-нибудь более существенного, иначе я вообще не вижу смысла в ключах и индексах.

O.: Более существенное - ты имеешь в виду графический интерфейс как в MS Access?

К сожалению, сам Access работает точно так же — достаточно нажать кнопочку "SQL", и вы увидите эти связи "с большой натяжкой".

В.: Просто мне проще программно все это делать, благо таблицы небольшие... Раз в самом MySQL это не работает.

O.: Через вложенные циклы, рекурсии? Скажи зачем тогда тебе вообще база данных и таблицы?

В.: Никаких вложенных циклов и рекурсий — читаю н-ое колмчество массивов и с ними уже работаю.... А делать связь на основе селекта — не совсем то что мне нужно...

O.: Правильно... храни все в файликах, тогда вообще вопросов не будет.

В общем, считаю своим долгом разложить по полочкам связи таблиц.

Кстати, насчёт связей - вот мнение признанного авторитета - Voodoo ;) : более существенное - это поддержка reference www.mysql.com/documentation/mysql/commented/manual.php?section=CREATE_TABLE

create table.....
reference_definition: REFERENCES tbl_name [(index_col_name,...)]
[MATCH FULL | MATCH PARTIAL]
[ON DELETE reference_option]
[ON UPDATE reference_option]

The FOREIGN KEY, CHECK, and REFERENCES clauses don't actually do anything. The syntax for them is provided only for compatibility, to make it easier to port code from other SQL servers and to run applications that create tables with references. See section 5.4 Functionality Missing from MySQL.

Связь без большой натяжки

Вообще, никакой натяжки в описании связей в запросе не было и нет - испокон веков БД работали именно так. Access со стрелочками и формопостроителями появился намного позднее.

Связь между таблицами - суть реляционной базы данных. В идеале вне базы данных не держится никакая информация. Внутри базы разные по сути вещи разделяются на разные таблицы - например, сообщения и форумы, сообщения и их авторы (если есть регистрация участников), возможно даже в отдельную таблицу выносится разрешения доступ к форумам персонально для каждого участника. При этом данные лежат там, где надо, не смешиваются друг с другом, и не повторяются лишний раз. Это и есть основной смысл реляционных БД. За исключением сложных задач (например, построить дерево обсуждений форума), выборка данных производится одним запросом. Никакие массивы использовать не надо.

Как делать неправильно

Например, вот так:


$res1 = mysql_query("SELECT id, name FROM rubs");
while ($row = mysql_fetch_row($res1))
  $rub[$row[0]] = $row[1];

Из запроса получены имена рубрик и записаны в массив $rub.


$res2 = mysql_query("SELECT id, url, name, rub FROM sites WHERE какое-то-там-условие");
while ($row = mysql_fetch_array($res2)) {
  echo "<a href=", $row["url"], ">", $row["name"], "</a>";
  echo "(рубрика <a href=rub.phtml?id=", $row["id"], ">";
  echo $rub[$row["rub"]], "</a><br>";
  };

Теперь выбираются новости, и вместо выбранного номера рубрики вставляем соответствующий элемент массива рубрик.

На самом деле, можно избавить себя от необходимости тащить сквозь всю программу этот массив $rub (а если у нас к рубрикам обращаются функции - что, через GLOBAL брать?), от возможности ошибиться с $rub[$row["rub"]] - если на странице несколько подобных запросов, то сделать опечатку где-нибудь легко.

Кроме того, под массив $rub требуется некоторый объем памяти (а если рубрик много?). В третьей версии PHP такой скрипт будет выполняться дольше, чем при использовании объединений таблиц, потому что он интерпретирует программу построчно при выполнении (в отличие от 4-го, который компилирует программу и только потом выполняет).

Как надо

В приведённом выше примере можно применить объединение таблиц и избавиться от описанных недостатков.

$res = mysql_query("SELECT sites.id, url, sites.name as sitename, rubs.name as rubsname, 
rubs.id as rub_id FROM sites, rubs WHERE sites.rub=rubs.id 
и-какое-то-там-условие");
while ($row = mysql_fetch_array($res2)) {
  echo "<a href=", $row["url"], ">", $row["sitename"], "</a>";
  echo "(рубрика <a href=rub.phtml?id=", $row["rub_id"], ">";
  echo $row["rubsname"], "</a><br>";
  };

Итак, здесь лучше использовать запрос "SELECT sites.id, url, sites.name as sitename, rubs.name as rubsname, rubs.id as rub_id FROM sites, rubs WHERE sites.rub=rubs.id". Получается, что мы имеем готовый массив, заботимся о выводе только его элементов и пишем меньше кода.

Синтаксис объединений таблиц

Простое соединение - INNER JOIN:

SELECT <fields> FROM table1 INNER JOIN table2 ON table1.field1=table2.field


или
SELECT <fields> FROM table1, table2 WHERE table1.field1=table2.field2


или
SELECT <fields> FROM table1 INNER JOIN table2 USING (field1)


если таблицы объединяются по полю field1.
В таком соединении выбираются только те строки таблиц, которые соответствуют условию объединения - равенство значений полей. Если для строки table1 нет соответствующей строки из table2, строка не попадает в итог запроса. Если же надо подсчитать количество сайтов в рубрике (продолжаю пример с каталогом), такой запрос не совсем подходит - в списке появятся только рубрики, в которых есть сайты. Для подобной операции нужно использовать LEFT JOIN.

SELECT <fields> FROM table1 LEFT JOIN table2 ON table1.field1=table2.field2


или
SELECT <fields> FROM table1 LEFT JOIN table2 USING (field1)


если таблицы объединяются по полю field1.
При этом соответствующей строки в table2 может и не быть, тогда в полях из table2 мы получим NULL, а если это групповая операция, как в случае с количеством сайтов в рубрике, тогда в поле будет 0:

SELECT rubs.id, name, COUNT(sites.id) AS sites FROM rubs LEFT JOIN sites ON rubs.id=sites.rub GROUP BY rubs.id


Заметьте: поля id есть в обеих таблицах, поэтому в их обозначении надо использовать имя таблицы. Кстати, если при объединении не используются групповые операции, всё равно лучше менять имя поля оператором AS, чтобы не возникало путаницы.

Добавлено: 31 Марта 2018 19:15:39 Добавил: Андрей Ковальчук