SUM
の動作に似たSELECT PRODUCT(table.price) FROM table GROUP BY table.sale
のようなものを探しています。
ドキュメンテーションで何かを見逃したことがありますか、それとも本当にPRODUCT
関数がありませんか?
もしそうなら、なぜですか?
注:postgres、mysql、mssqlで関数を探しましたが、どれも見つかりませんでしたので、すべてのsqlでサポートされていないと仮定しました。
SQL標準にはPRODUCT
set関数はありません。ただし、(たとえば、CONCATENATE
set関数とは異なり、SQLには適していません。たとえば、結果のデータ型には複数の値が含まれ、最初の標準形式に関して問題が発生します) 。
SQL Standardsは、1990年頃のSQL製品全体の機能を統合し、将来の開発で「思考リーダーシップ」を提供することを目的としています。要するに、彼らはSQLが何をし、SQLが何をすべきかを文書化します。 PRODUCT
set関数がないことは、1990年に含める価値があるベンダーがなく、標準に導入することに学術的な関心がなかったことを示唆しています。
もちろん、ベンダーは常に独自の機能を追加しようとしていますが、最近では通常、接線ではなく標準への拡張として使用されています。私が使用したSQL製品のいずれかでPRODUCT
set関数(またはそれに対する要求さえ)を見たことを覚えていません。
いずれの場合でも、log
set関数でexp
およびSUM
スカラー関数(およびネガを処理するロジック)を使用すると、回避策は非常に簡単です。サンプルコードについては、@ gbnの回答をご覧ください。ただし、ビジネスアプリケーションでこれを行う必要はありません。
結論として、私の最良の推測は、SQLエンドユーザーからPRODUCT
set関数に対する要求がないということです。さらに、学術的な興味を持つ人なら誰でも、おそらく回避策を受け入れられると思うでしょう(つまり、PRODUCT
set関数が提供する構文糖を評価しません)。
興味深いことに、SQL Server Landには新しいセット関数が必要ですが、ウィンドウ関数の種類(および標準SQLも)が必要です。さらなる需要に関与する方法など、詳細については、 Itzik Ben-Ganのブログ を参照してください。
MSSQLの場合、これを使用できます。それは他のプラットフォームに採用することができます:それは単に対数の数学と集計です。
SELECT
GrpID,
CASE
WHEN MinVal = 0 THEN 0
WHEN Neg % 2 = 1 THEN -1 * EXP(ABSMult)
ELSE EXP(ABSMult)
END
FROM
(
SELECT
GrpID,
--log of +ve row values
SUM(LOG(ABS(NULLIF(Value, 0)))) AS ABSMult,
--count of -ve values. Even = +ve result.
SUM(SIGN(CASE WHEN Value < 0 THEN 1 ELSE 0 END)) AS Neg,
--anything * zero = zero
MIN(ABS(Value)) AS MinVal
FROM
Mytable
GROUP BY
GrpID
) foo
ここでの私の答えから: SQL Serverクエリ-グループごとの乗算
なぜないのかはわかりませんが、(負の数にはもっと注意してください)ログと指数を使用して行うことができます:-
select exp (sum (ln (table.price))) from table ...
製品の集計関数を実行できますが、このように自分で計算する必要があります...
SELECT
Exp(Sum(IIf(Abs([Num])=0,0,Log(Abs([Num])))))*IIf(Min(Abs([Num]))=0,0,1)*(1-2*(Sum(IIf([Num]>=0,0,1)) Mod 2)) AS P
FROM
Table1
T-SQLには、一連の行の文字列値を1つの変数に連結できるようにする巧妙なトリックがあります(ANSIかどうかはわかりません)。乗算にも機能するようです:
declare @Floats as table (value float)
insert into @Floats values (0.9)
insert into @Floats values (0.9)
insert into @Floats values (0.9)
declare @multiplier float = null
select
@multiplier = isnull(@multiplier, '1') * value
from @Floats
select @multiplier
これは、log/expソリューションよりも数値的に安定している可能性があります。
それは、多くの製品に対応できるナンバリングシステムがないためだと思います。データベースは多数のレコード用に設計されているため、1000個の数の積は非常に大きくなり、浮動小数点数の場合、伝播されるエラーは膨大になります。
また、ログの使用は危険なソリューションになる可能性があることに注意してください。数学的にlog(a * b)= log(a)* log(b)ですが、実数を扱っていないため、コンピューターにはない可能性があります。 a * bではなく2 ^(log(a)+ log(b))を計算すると、予期しない結果が生じる可能性があります。例えば:
SELECT 9999999999 * 99999999974482、EXP(LOG(9999999999)+ LOG(99999999974482))
sQL Serverの戻り値
999999999644820000025518、9.999999999644812E + 23
だから私のポイントは、あなたが製品を慎重にやろうとしているときであり、テストは重いです。
この問題に対処する1つの方法(スクリプト言語で作業している場合)は、group_concat関数を使用することです。たとえば、SELECT group_concat(table.price) FROM table GROUP BY table.sale
これは、同じ販売価格のすべての価格をコンマで区切った文字列を返します。次に、パーサーを使用して各価格を取得し、乗算を実行できます。 (phpでは、実際にはarray_reduce関数を使用することもできます。実際には php.netマニュアル に適切な例があります)。
乾杯
1つのアプローチは、次の2つの組み込み関数を組み合わせることです。
SELECT table.sale, AVG(table.price) * COUNT(table.price) FROM table GROUP BY table.sale
デカルト積のカーディナリティは特定のセットのカーディナリティの積であるという事実に基づく別のアプローチ;-)
⚠警告:この例は単なる楽しみのためであり、かなり学術的なものです。実稼働では使用しないでください!(事実上、正の実際に小さな整数のためだけです)⚠
with recursive t(c) as (
select unnest(array[2,5,7,8])
), p(a) as (
select array_agg(c) from t
union all
select p.a[2:]
from p
cross join generate_series(1, p.a[1])
)
select count(*) from p where cardinality(a) = 0;
この問題は、ウィンドウ関数やCTEなどの最新のSQL機能を使用して解決できます。すべてが標準SQLであり、対数ベースのソリューションとは異なり、整数の世界から浮動小数点の世界への切り替えや非正数の処理は必要ありません。行に番号を付け、行がなくなるまで再帰クエリで製品を評価します。
with recursive t(c) as (
select unnest(array[2,5,7,8])
), r(c,n) as (
select t.c, row_number() over () from t
), p(c,n) as (
select c, n from r where n = 1
union all
select r.c * p.c, r.n from p join r on p.n + 1 = r.n
)
select c from p where n = (select max(n) from p);
あなたの質問は販売コラムによるグループ化を伴うので、事態は少し複雑になりましたが、それでも解決可能です:
with recursive t(sale,price) as (
select 'multiplication', 2 union
select 'multiplication', 5 union
select 'multiplication', 7 union
select 'multiplication', 8 union
select 'trivial', 1 union
select 'trivial', 8 union
select 'negatives work', -2 union
select 'negatives work', -3 union
select 'negatives work', -5 union
select 'look ma, zero works too!', 1 union
select 'look ma, zero works too!', 0 union
select 'look ma, zero works too!', 2
), r(sale,price,n,maxn) as (
select t.sale, t.price, row_number() over (partition by sale), count(1) over (partition by sale)
from t
), p(sale,price,n,maxn) as (
select sale, price, n, maxn
from r where n = 1
union all
select p.sale, r.price * p.price, r.n, r.maxn
from p
join r on p.sale = r.sale and p.n + 1 = r.n
)
select sale, price
from p
where n = maxn
order by sale;
結果:
sale,price
"look ma, zero works too!",0
multiplication,560
negatives work,-30
trivial,8
Postgresでテスト済み。