Нетривиальные задачи Sql

Discussion in 'PHP' started by VDShark, 26 Jun 2008.

  1. VDShark

    VDShark Elder - Старейшина

    Joined:
    1 Feb 2007
    Messages:
    260
    Likes Received:
    158
    Reputations:
    62
    Да... у меня там самосоединение было + условная конструкция =)
     
  2. VDShark

    VDShark Elder - Старейшина

    Joined:
    1 Feb 2007
    Messages:
    260
    Likes Received:
    158
    Reputations:
    62
    Ну... коль никто не пишет - предложу еще одну задачку. Не такая интересная как предыдущая - но здесь задачка немног другая - максимально упростить решение.
    Только что придумал условия - но прием, применяемый в данной задаче, достаточно распространенный.


    Вот дамп базы:
    Code:
    --
    -- Структура таблицы `fruits`
    --
    
    CREATE TABLE IF NOT EXISTS `fruits` (
      `name` varchar(50) NOT NULL,
      PRIMARY KEY  (`name`)
    ) ENGINE=InnoDB DEFAULT CHARSET=cp1251;
    
    --
    -- Дамп данных таблицы `fruits`
    --
    
    INSERT INTO `fruits` (`name`) VALUES
    ('apple'),
    ('banana'),
    ('orange'),
    ('pear');
    
    Задача следующая: получить все пары значений (т.е. в результирующей выборке будет 2 столбца), подпадающие под след. условия:
    1) Одного и того же фрукта в обоих столбцах быть не должно
    2) Кортежи, в которых ячейки поменяны местами считаются одинаковыми. Т.е. если у нас есть
    ------------------
    | banana | apple |
    ------------------
    то
    ------------------
    | apple | banana |
    ------------------
    уже быть не должно :)

    Ждем максимально компактного решения :)
     
  3. z3r0cool

    z3r0cool Elder - Старейшина

    Joined:
    22 Apr 2008
    Messages:
    32
    Likes Received:
    11
    Reputations:
    0
    Вот так!

    Code:
    SELECT *
    	FROM fruits AS f1
    		JOIN fruits AS f2 ON f2.name > f1.name
    	ORDER BY f1.name
    
     
    2 people like this.
  4. VDShark

    VDShark Elder - Старейшина

    Joined:
    1 Feb 2007
    Messages:
    260
    Likes Received:
    158
    Reputations:
    62
    Да, молодец... В общем правильный подход.
    Только
    Code:
    SELECT *
    FROM `fruits` as `f1`,`fruits` as `f2`
    WHERE `f1`.`name`>`f2`.`name`
    
    данный код (через декартово произведение) в этом конкретном случае отработает чуточку быстрее (0.0003 сек. против 0.0008 сек).
    Хотя с увеличением таблицы эта разница конечно же будет незаметна, а при некоторой оптимизации скорее всего будет обратная ситуация :)
     
  5. VDShark

    VDShark Elder - Старейшина

    Joined:
    1 Feb 2007
    Messages:
    260
    Likes Received:
    158
    Reputations:
    62
  6. MasterMushi

    MasterMushi Member

    Joined:
    19 Dec 2007
    Messages:
    29
    Likes Received:
    5
    Reputations:
    3
    Я так до конца и не понял как красиво решить задачу с компутерными кусками. Хотябы проще возьмем из моей практики. Есть 2 таблицы.

    В одной написаны
    Id | Id_Получатель | Id_Отправитель | Текст сообщения

    Во второй
    Id | Имя.

    Нужно на выходе получить структуру
    Id_сообщения | Имя 1 отправителя | Имя 2 получателя | Текст сообщения

    Как сделать такую выборку единым запросом?
     
  7. VDShark

    VDShark Elder - Старейшина

    Joined:
    1 Feb 2007
    Messages:
    260
    Likes Received:
    158
    Reputations:
    62
    В твоем приме ничего нетривиального нет :) А для таких вопросов существует спец. топик, с вопросами по sql... Так что просьба не захламлять топик =\
     
  8. VDShark

    VDShark Elder - Старейшина

    Joined:
    1 Feb 2007
    Messages:
    260
    Likes Received:
    158
    Reputations:
    62
    Давно никто ничего не выкладывал здесь. А жаль. Разминка для мозга полезна ;)
    Ну что ж - выложу небольшую задачку. Она достаточно проста, но как говорится на безрыбье... В общем вы поняли =)
    На данную задачку меня натолкнул следующий факт: все чащи люди неправильно проектируют структуру БД, а потом страдают.
    Возьмем пример:
    Code:
    login     |     param     |   value
    --------------------------------------
    Alex      |   City        |   London
    Alex      |   RegDate     |   22-04-2009
    Alex      |   Email       |   [email protected]
    Nata      |   City        |   Paris
    Nata      |   RegDate     |   15-07-2009
    Nata      |   Email       |   [email protected]
    
    Налицо безграмотнейшее составление структуры. Отношение не находится даже в 1НФ.
    И как следствие у людей начинаются большие проблемы с составлением запросов =)
    И начинают они плакаться в соцсетях с просьбами о помощи.
    Так вот - нормальную структуру, допустим, выяснили, осталось сконвертить для нее данные.
    Структура:
    Code:
    login | city | regdate | email
    Т.е. задача состоит в том, что бы из вышеприведенной таблички одним запросом получить данные в формате, приведенном выше.

    Дамп базы (для ленивых, что б не создавать структуру и не писать инсерты):
    Code:
    CREATE TABLE IF NOT EXISTS `tst` (
      `login` varchar(20) NOT NULL DEFAULT '',
      `param` varchar(10) NOT NULL DEFAULT '',
      `value` varchar(20) DEFAULT NULL,
      PRIMARY KEY (`login`,`param`)
    );
    
    INSERT INTO `tst` (`login`, `param`, `value`) VALUES
    ('Alex', 'City', 'London'),
    ('Alex', 'Email', '[email protected]'),
    ('Alex', 'RegDate', '22-04-2009'),
    ('Nata', 'City', 'Paris'),
    ('Nata', 'Email', '[email protected]'),
    ('Nata', 'RegDate', '15-07-2009');
    
    Задача решается не одним способом =) Итак - разомните мозги, отложите на минутку свой "набор кавычек".
     
  9. oRb

    oRb Elder - Старейшина

    Joined:
    9 May 2008
    Messages:
    294
    Likes Received:
    582
    Reputations:
    256
    Code:
    mysql> SELECT t1.login, t1.value as city, t2.value as regdate, t3.value as email FROM `tst` t1
        -> LEFT JOIN `tst` t2 ON t1.login = t2.login AND t2.param='regdate'
        -> LEFT JOIN `tst` t3 ON t1.login = t3.login AND t3.param='email'
        -> GROUP BY t1.login;
    +-------+--------+------------+-----------------+
    | login | city   | regdate    | email           |
    +-------+--------+------------+-----------------+
    | Alex  | London | 22-04-2009 | [email protected] | 
    | Nata  | Paris  | 15-07-2009 | [email protected]   | 
    +-------+--------+------------+-----------------+
    2 rows in set (0,00 sec)
    Code:
    mysql> SELECT login,
        -> (SELECT `value` FROM `tst` WHERE `login`=t1.login AND `param`='city') as city,
        -> (SELECT `value` FROM `tst` WHERE `login`=t1.login AND `param`='regdate') as regdate,
        -> (SELECT `value` FROM `tst` WHERE `login`=t1.login AND `param`='email') as email
        -> FROM `tst` t1
        -> GROUP BY `login`;
    +-------+--------+------------+-----------------+
    | login | city   | regdate    | email           |
    +-------+--------+------------+-----------------+
    | Alex  | London | 22-04-2009 | [email protected] | 
    | Nata  | Paris  | 15-07-2009 | [email protected]   | 
    +-------+--------+------------+-----------------+
    2 rows in set (0,01 sec)
     
    1 person likes this.
  10. VDShark

    VDShark Elder - Старейшина

    Joined:
    1 Feb 2007
    Messages:
    260
    Likes Received:
    158
    Reputations:
    62
    oRb - молодец.
    Остальные - еще остались способы =) Пишем - не стесняемся :)
     
  11. VDShark

    VDShark Elder - Старейшина

    Joined:
    1 Feb 2007
    Messages:
    260
    Likes Received:
    158
    Reputations:
    62
    Итак, никто не хочет остальные методы решения для прошлой задачи придумывать - ну и ладно =)
    Еще одна задачка. На мой взгляд более интересная.
    Имеется в наличии такая вот табличка
    Code:
    --------------------------------
      model  |    name    |  maker   
    --------------------------------
      1035	 |  pliers    |  F
      1103	 |  pestle    |  A
      1104	 |  key       |  G
      1132	 |  nippers   |  G
      1273	 |  gavel     |  F
      1314	 |  chisel    |  A
      1402	 |  mortar    |  B
      1561	 |  bottle    |  F
    
    Необходимо добавить столбец с номерами строк (т.е. номер кортежа - и собственно кортеж), в порядке возрастания модели.
    Пользоваться можно ТОЛЬКО средствами, предусмотренными стандартом SQL-92 (т.е. без всяких rank(), row_number() и подобных функций), а так же без временных таблиц и иже с ними.
    По хорошему - это один запрос (ну, возможно с коррелирующим подзапросом, но можно сделать и без него).
    В итоге мы должны получить примерно следующее:
    Code:
    ----------------------------------------
      num |  model  |    name    |  maker   
    ----------------------------------------
      1   |  1035   |    pliers  |  F 
      2   |  1103   |    pestle  |  A
      ... |  ...    |    ...     |  ...   
    
    Ну и по традиции дамп:
    Code:
    CREATE TABLE `tst2` (
    `model` INT( 3 ) NOT NULL ,
    `name` VARCHAR( 20 ) NOT NULL ,
    `maker` CHAR( 1 ) NOT NULL ,
    PRIMARY KEY ( `model` ) 
    );
    
    INSERT INTO `tst2` VALUES
    ('1104','key','G'),
    ('1273','gavel','F'),
    ('1035','pliers','F'),
    ('1132','nippers','G'),
    ('1314','chisel','A'),
    ('1103','pestle','A'),
    ('1402','mortar','B'),
    ('1561','bottle','F')
    
     
  12. VDShark

    VDShark Elder - Старейшина

    Joined:
    1 Feb 2007
    Messages:
    260
    Likes Received:
    158
    Reputations:
    62
    Неужто никому размять мозги не хочется? Задачка то плевая.
    Или все "к0вычк0ми" мозг забили? :)
     
    1 person likes this.
  13. Pashkela

    Pashkela Динозавр

    Joined:
    10 Jan 2008
    Messages:
    2,750
    Likes Received:
    1,044
    Reputations:
    339
    нет, ну почему, интересно, просто на практике нафиг такое никогда не понадобиться. Лучше бы чонить жизненное, что могло бы хоть когда нибудь пригодиться.
     
  14. VDShark

    VDShark Elder - Старейшина

    Joined:
    1 Feb 2007
    Messages:
    260
    Likes Received:
    158
    Reputations:
    62
    А представь прийдется поработать со старой СУБД? В жизни всякое бывает =) Относительно недавно это не так уж и редко использовалось на практике.
     
  15. astrologer

    astrologer Elder - Старейшина

    Joined:
    30 Aug 2007
    Messages:
    837
    Likes Received:
    267
    Reputations:
    59
    [font=Consolas,monospace]<spoiler>
    select count(*) as num, current.*
    from test as current, test
    where current.model >= test.model
    group by current.model
    order by current.model asc

    </spoiler>[/font]
     
    1 person likes this.
  16. VDShark

    VDShark Elder - Старейшина

    Joined:
    1 Feb 2007
    Messages:
    260
    Likes Received:
    158
    Reputations:
    62
    astrologer - молодец =)
     
  17. VDShark

    VDShark Elder - Старейшина

    Joined:
    1 Feb 2007
    Messages:
    260
    Likes Received:
    158
    Reputations:
    62
    На этот раз проверим как у нас обстоят дела с работой со строками.
    Задача:
    есть таблица с единственным полем, в котором записаны cтроки вида

    Code:
    sdfdf_efgregehmtri_ref_WEFEW_reg_regbb
    На выходе нужно заменить все что находится между первым и последним символами "_" на "*".

    Т.е. по вышеприведенной строке должно получится такое:
    Code:
    sdfdf_**************************_regbb
    Вот небольшой дамп (хотя легче самим что нить набить (предварительно подумав), дабы отловить некоторые "подводные камни" и т.п.):
    Code:
    CREATE TABLE IF NOT EXISTS `tst` (
      `field` varchar(40) NOT NULL
    );
    
    INSERT INTO `tst` (`field`) VALUES
    ('aaaa_wd_regr_dew_bbbb'),
    ('asd_sed_fergf')
    
    Реализации для конкретных СУБД могут немного отличаться, но суть от этого не меняется.

    upd: естественно как всегда ответ - единственный запрос. Без рекурсий и тп. Так же никаких хп. Ну в общем вы поняли =)
     
  18. krypt3r

    krypt3r Elder - Старейшина

    Joined:
    27 Apr 2007
    Messages:
    1,507
    Likes Received:
    389
    Reputations:
    101
    хз, это, наверно, извращенный метод, но что пришло в голову =)
    Code:
    select concat_ws('_', 
      substring_index(url, '_', 1), 
      repeat('*', length(url) - length(substring_index(url, '_', 1)) - 
        length(substring_index(url, '_', -1)) - 2), 
      substring_index(url, '_', -1)) as str from t1;
    
     
    1 person likes this.
  19. VDShark

    VDShark Elder - Старейшина

    Joined:
    1 Feb 2007
    Messages:
    260
    Likes Received:
    158
    Reputations:
    62
    В принципе ход мысли правильный... Хотя ничего особо сложного в данной задачке нет =) Но все равно молодец)