Является ли SQL GROUP BY недостатком дизайна?

Есть еще одна проблема, которая не указана ни в одном из существующих ответов. Python разрешено объединять любые два неизменных значения, и предварительно созданные значения малых значений не являются единственным способом, которым это может случиться. Реализация Python никогда не гарантирована , но все они делают это не более, чем просто малые int.


Во-первых, есть еще некоторые предварительно созданные такие как пустые tuple, str и bytes и некоторые короткие строки (в CPython 3.6 это 256 односимвольных строк Latin-1). Например:

>>> a = ()
>>> b = ()
>>> a is b
True

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

>>> c = 257
>>> d = 257
>>> c is d
False
>>> e, f = 258, 258
>>> e is f
True

И это не ограничено значениями int:

>>> g, h = 42.23e100, 42.23e100
>>> g is h
True

Очевидно, что CPython не поставляется с предварительно созданным float для параметра 42.23e100. Итак, что здесь происходит?

Компилятор CPython будет объединять постоянные значения некоторых известных неизменяемых типов, таких как int, float, str, bytes, в одном модуле компиляции. Для модуля весь модуль является единицей компиляции, но в интерактивном интерпретаторе каждый оператор представляет собой отдельный блок компиляции. Поскольку c и d определены в отдельных утверждениях, их значения не объединяются. Поскольку e и f определены в том же самом заявлении, их значения сливаются.


Вы можете видеть, что происходит, разобрав байт-код. Попробуйте определить функцию, которая выполняет e, f = 128, 128, а затем называет dis.dis на ней, и вы увидите, что существует одно постоянное значение (128, 128)

>>> def f(): i, j = 258, 258
>>> dis.dis(f)
  1           0 LOAD_CONST               2 ((128, 128))
              2 UNPACK_SEQUENCE          2
              4 STORE_FAST               0 (i)
              6 STORE_FAST               1 (j)
              8 LOAD_CONST               0 (None)
             10 RETURN_VALUE
>>> f.__code__.co_consts
(None, 128, (128, 128))
>>> id(f.__code__.co_consts[1], f.__code__.co_consts[2][0], f.__code__.co_consts[2][1])
4305296480, 4305296480, 4305296480

. Вы можете заметить, что компилятор сохранил 128 как константу, даже если он фактически не используется байтовым кодом, что дает вам представление о том, как мало оптимизирует компилятор CPython. Это означает, что (непустые) кортежи на самом деле не сливаются:

>>> k, l = (1, 2), (1, 2)
>>> k is l
False

Поместите это в функцию, dis, и посмотрите на co_consts - есть 1 и 2, два (1, 2) кортежа, которые имеют одинаковые 1 и 2, но не идентичны, и кортеж ((1, 2), (1, 2)), который имеет два разных одинаковых кортежа.


Есть еще одна оптимизация, которую выполняет CPython: string interning. В отличие от сгибания константы компилятора, это не ограничивается литералами исходного кода:

>>> m = 'abc'
>>> n = 'abc'
>>> m is n
True

С другой стороны, он ограничен типом str и строками типа внутреннего хранилища «ascii compact», «compact» или «legacy ready» , и во многих случаях только «ascii compact» будет интернирован.


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

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

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

Или, другими словами, использовать is только для проверки документированных синглетов (например, None) или которые создаются только в одном месте в код (например, идиома _sentinel = object()).

30
задан Kheldar 10 September 2011 в 14:07
поделиться

8 ответов

Вам не обязательно группировать по тому же самому, что вы выбираете, например :

SQL:select priority,count(*) from rule_class
group by priority

PRIORITY COUNT(*) 70 1 50 4 30 1 90 2 10 4

SQL:select decode(priority,50,'Norm','Odd'),count(*) from rule_class group by priority

DECO COUNT(*) Odd 1 Norm 4 Odd 1 Odd 2 Odd 4

SQL:select decode(priority,50,'Norm','Odd'),count(*) from rule_class group by decode(priority,50,'Norm','Odd')

DECO COUNT(*) Norm 4 Odd 8

11
ответ дан 28 November 2019 в 00:21
поделиться

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

Также: трудно придумать пример, где было бы полезно иметь более длинный список столбцов в списке групп, чем список выбора. Лучшее, что я могу придумать, это ...

create table people
(  Nam char(10)
  ,Adr char(10)
)

insert into people values ('Peter', 'Tibet')
insert into people values ('Peter', 'OZ')
insert into people values ('Peter', 'OZ')

insert into people values ('Joe', 'NY')
insert into people values ('Joe', 'Texas')
insert into people values ('Joe', 'France')

-- Give me people where there is a duplicate address record

select * from people where nam in 
(
select nam              
from People        
group by nam, adr        -- group list different from select list
having count(*) > 1
)
3
ответ дан 28 November 2019 в 00:21
поделиться

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

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

select  id, count(a), b, c, d
from    table
group by
        id, b, c, d

становится

select  id, myCount, b, c, d
from    table t
        inner join (
            select id, count(*) as myCount
            from table
            group by id
        ) as myCountTable on myCountTable.id = t.id

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

3
ответ дан 28 November 2019 в 00:21
поделиться

Если вы хотите упростить написание скриптов. Вот один совет:

В MS SQL MGMS напишите запрос в тексте, что-то вроде select * from my_table После этого выделите текст правой кнопкой мыши и нажмите "Design Query in Editor...". Sql studio откроет новый редактор с заполненными всеми полями, после чего снова щелкните правой кнопкой мыши и выберите "Add Gruop BY". Sql MGM studio добавит код для вас.

Я считаю этот метод чрезвычайно полезным для операторов вставки. Когда мне нужно написать скрипт для вставки большого количества полей в таблицу, я просто делаю select * from table_where_want_to_insert и после этого меняю тип оператора select на insert,

2
ответ дан 28 November 2019 в 00:21
поделиться

I Agree

Я вполне согласен с вопросом. Я задавал такой же здесь.

Честно говоря, я думаю, что это недостаток языка.

Я понимаю, что есть аргументы против этого, но в реальном мире мне еще не приходилось использовать предложение GROUP BY, содержащее что-то кроме всех неагрегированных полей из предложения SELECT.

2
ответ дан 28 November 2019 в 00:21
поделиться

Атрибуты "superflous" влияют на упорядочивание результата.

Рассмотрим:

create table gb (
  a number,
  b varchar(3),
  c varchar(3)
);

insert into gb values (   3, 'foo', 'foo');
insert into gb values (   1, 'foo', 'foo');
insert into gb values (   0, 'foo', 'foo');

insert into gb values (  20, 'foo', 'bar');
insert into gb values (  11, 'foo', 'bar');
insert into gb values (  13, 'foo', 'bar');

insert into gb values ( 170, 'bar', 'foo');
insert into gb values ( 144, 'bar', 'foo');
insert into gb values ( 130, 'bar', 'foo');

insert into gb values (2002, 'bar', 'bar');
insert into gb values (1111, 'bar', 'bar');
insert into gb values (1331, 'bar', 'bar');

Это утверждение

select sum(a), b, c
  from gb
group by b, c;

приводит к

    44 foo bar
   444 bar foo
     4 foo foo
  4444 bar bar

а это

select sum(a), b, c
  from gb
group by c, b;

приводит к

   444 bar foo
    44 foo bar
     4 foo foo
  4444 bar bar
0
ответ дан 28 November 2019 в 00:21
поделиться

Мне нужна функция , которая поддерживает локальное состояние в Ruby.

Это слово «функция» должно немедленно вызвать большой жирный красный мигающий предупреждающий знак о том, что вы используете неправильный язык программирования. Если нужны функции, следует использовать функциональный язык программирования, а не объектно-ориентированный. В функциональном языке программирования функции обычно закрываются над своей лексической средой, что делает то, что вы пытаетесь сделать абсолютно тривиальным:

var state;
function incMult(factor) {
    if (state === undefined) {
        state = 0;
    }
    state += 1;
    return factor * state;
}
print(incMult(2)); // => 2
print(incMult(2)); // => 4
print(incMult(2)); // => 6

Этот конкретный пример находится в ECMAScript, но выглядит более или менее одинаково в любом функциональном языке программирования.

[Примечание: Я знаю, что это не очень хороший пример, потому что ECMAScript на самом деле также объектно-ориентированный язык и потому, что у него нарушена семантика объема, которая фактически означает, что состояние утечка в этом случае тоже. В языке с правильной семантикой размаха (и через пару лет ECMAScript будет одним из них), это будет работать по назначению. Я использовал ECMAScript главным образом для своего знакомого синтаксиса, а не как пример хорошего функционального языка.]

Это то, как состояние инкапсулируется в функциональные языки, так как, ну, так как есть функциональные языки, вплоть до лямбда-исчисления.

Однако в 1960-х годах некоторые умные люди заметили, что это очень распространённый образец, и решили, что этот образец настолько распространён, что она заслуживает собственной языковой особенности. И таким образом родился объект .

Таким образом, в объектно-ориентированном языке вместо использования функциональных замыканий для инкапсуляции состояния используются объекты. Как вы, возможно, заметили, методы в Ruby не закрываются над их лексической средой, в отличие от функций в функциональных языках программирования. И именно в этом причина: потому что инкапсуляция состояния достигается другими средствами.

Итак, в Ruby вы бы использовали такой объект:

inc_mult = Object.new
def inc_mult.call(factor)
  @state ||= 0
  @state += 1
  factor * @state
end
p inc_mult.(2) # => 2
p inc_mult.(2) # => 4
p inc_mult.(2) # => 6

[Sidenote: Это соответствие 1:1 - это то, о чем говорят функциональные программисты, когда говорят, что «объекты - это просто закрытие бедняков». Конечно, объектно-ориентированные программисты обычно противостоят с «замыканиями - это просто объекты бедняки». И самое забавное, что оба они правы, и ни один из них не осознает это.]

Теперь, ради полноты, я хочу отметить, что, хотя методы не закрываются над их лексической средой, есть одна конструкция в Ruby, которая делает : блоки. (Интересно, что блоки не являются объектами.) Так как вы можете определять методы с помощью блоков, вы также можете определить методы, которые являются замыканиями:

foo = Object.new
state = nil
foo.define_singleton_method :inc_mult do |factor|
  state ||= 0
  state += 1
  factor * state
end
p foo.inc_mult(2) # => 2
p foo.inc_mult(2) # => 4
p foo.inc_mult(2) # => 6
-121--4903716-

Этот поток содержит некоторые полезные объяснения.

http://social.msdn.microsoft.com/Forums/en/transactsql/thread/52482614-bfc8-47db-b1b6-deec7363bd1a

-121--1339657-

Я бы сказал, что это скорее язык, проектировать выбор, чтобы решения были явными, а не неявными. Например, что, если я хочу сгруппировать данные в порядке, отличном от того, в котором я выводю столбцы?Или если я хочу сгруппировать по столбцам, которые не включены в выбранные столбцы? Или если я хочу выводить только сгруппированные столбцы и не использовать агрегатные функции? Только путем явного указания моих предпочтений в группе по пунктам мои намерения ясны.

Также необходимо помнить, что SQL - очень старый язык (1970). Посмотрите, как Linq перевернул все вокруг, чтобы заставить Intellisense работать - это выглядит очевидным для нас сейчас, но SQL предшествует IDE и поэтому не мог принять во внимание такие проблемы.

1
ответ дан 28 November 2019 в 00:21
поделиться

Эта ветка предоставляет несколько полезных объяснений.

http://social.msdn.microsoft.com/Forums/en/transactsql/thread/52482614-bfc8-47db-b1b6-deec7363bd1a

1
ответ дан 28 November 2019 в 00:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: