Movatterモバイル変換


[0]ホーム

URL:


SQLServerCentral Article
SQLServerCentral Article

T-SQL in SQL Server 2025: The PRODUCT Function

,

Earlier this year at SQL Saturday Austin 2025, Conor Cunningham gave a keynote that discussed the engineering efforts in the Austin office around SQL Server. One of the things he mentioned was PRODUCT(), which was written there and added to SQL Server 2025 to help with the GDP calculation for the US government.

Yep, that's right, a function was added since SQL Server is used in this calculation. I'm sure other customers will use this, but I decided to take a look at this function.

With a new version of SQL Server coming, I wanted to cover some of the T-SQL code changes. This is part of a series on how the T-SQL language is evolving in this version.

Note: some of these changes are already available in the various Azure SQL products.

PRODUCT()

ThePRODUCT() function is similar to SUM() in that it is an aggregate that performs a mathematical calculation. SUM() works like this. Let's assume I have this table, called dbo.Numbers:

CREATE TABLE dbo.Numbers(    n INT);GOINSERT dbo.Numbers(    n)VALUES(1  ),(2),(3),(4);

If I run the query below, I get this calculation: 1 + 2 + 3 +4 = 10

SELECT SUM(n) from dbo.Numbers

Most of us know this.

In SQL Server 2025, I have a PRODUCT() function that works in a similar way. In this case, if I replace SUM with PRODUCT, I get a result of 24 (1 * 2 * 3 * 4).

product function sample

Easy, but why do this?

A Useful Example

There are lots of financial calculations that might want to implement a product-type function. As an example, suppose that I want to calculate the value of something based on inflation. Suppose I have inflation rate for a country each year and I'm curious what the effect is on their currency over time.

I have a table and some data from the UK, which is where Redgate is headquartered.

CREATE TABLE dbo.Inflation(    Country CHAR(3),    TrackedYear INT,    InflationRate NUMERIC(4, 2))GOINSERT dbo.Inflation(    Country,    TrackedYear,    InflationRate)VALUES('UK', 2019, 1.8),('UK', 2020, 0.9),('UK', 2021, 2.6),('UK', 2022, 9.1),('UK', 2023, 6.8),('UK', 2024, 3.8);

Suppose I want to know how the yen has changed in value since 2019? Before product, what I would do is combine a few functions like this:

SELECT EXP(SUM(LOG(1 + (inflationRate / 100.0))))FROM dbo.InflationWHERE country = 'UK';

This gives me a result.

Old style depreciation result

However, with PRODUCT, this is simpler.

SELECT PRODUCT(1 + (InflationRate / 100.0))FROM dbo.InflationWHERE Country = 'UK';

I get a similar result due to rounding, but I'd argue this is much easier to read.

Calculation result with PRODUCT

PRODUCT Syntax and Usage

The syntax for this function is fairly simple. the PRODUCT() function takes a single parameter, which is the expression that is used. You can optionally include the ALL and DISTICT keywords. The official syntax diagram is:

PRODUCT( [ALL | DISTINCT] expression)

Or, if you use this as a window function:

PRODUCT ( [ ALL] expression) OVER ([PARTITION BY clause] ORDER BY clause)

Above we have seen the basic queries that use PRODUCT. What about the ALL/Distinct? Let's look at these.

ALL and DISTINCT

First, ALL. I'll add a NULL value and then run Product() with and without the ALL keywork. No difference here, as ALL is the default.

Using ALL

The Distinct keyword is the optional part. If I change ALL to DISTINCT, and add a duplicate "4" value, I get this.

Ignoring duplicates

Notice that the first value (which is using ALL by default) returns 96. This is 1 * 2 * 3 * 4 *  4 with the NULL ignored. The second is distinct with the second "4" being ignored with the NULL. Good if you really want distinct values in your calculation. Bad if you were doing something like the inflation calculation above, where there can be the duplicate values across years.

The Window Version

We can also use PRODUCT as a Window function. First, let me add some data to my Inflation table. I'll add a few other countries.

INSERT dbo.Inflation(    Country,    TrackedYear,    InflationRate)VALUES('USA', 2019, 2.3),('USA', 2020, 1.4),('USA', 2021, 7.0),('USA', 2022, 6.5),('USA', 2023, 3.4),('USA', 2024, 2.9);GOINSERT dbo.Inflation(    Country,    TrackedYear,    InflationRate)VALUES('AUS', 2019, 1.6),('AUS', 2020, 0.8),('AUS', 2021, 2.9),('AUS', 2022, 6.6),('AUS', 2023, 5.6),('AUS', 2024, 3.2);

Now, let's call PRODUCT with the OVER() clause. If I use nothing in there, I get this:

Empty over clause

This is interesting as the docs say ORDER BY is required. I've submitted a PR to correct this as it clearly isn't required. If I add an ORDER BY, I get values in the order, though calculation changes with the default framing. I'm not sure these results really mean anything now.

Adding an ORDER BY to the query of depreciation

To really have useful values, I'd want a PARTITION BY since this data is really only useful by country. I'll also add the year to the results so we can see what is happening. Now I can compare some values to see what is happening by country.

paritioning query by country

We can see the US currency seems to have lost more purchasing than AUS, but less than the UK.

This is with the default framing, though it doesn't seem to matter with this function. The values are the same with ROWS and RANGE framing for this calculation.

testing framing with PRODUCT

I would assume rows and range matter here as they do for other functions. I haven't investigated extensively, but merely wanted to test if they were allowed as they aren't listed in the documentation.

Return Types

One of the interesting things about this function is that values can grow quickly. That can me large return types. There isa table that lists the data types returned based on the expression. Most of these make sense, with a larger type being returned than is passed in. The exception is INT. If an INT is in the expression, then the return type is an INT. I'd have expected a BIGINT here.

I've reproduced the table in summary form here.

  • INT returned if txpression is tinyint, smallint, or int
  • BIGINT returned if the expression is BIGINT
  • decimal(38,0) returned if the expression is decimal (or numeric) with scale == 0
  • decimal(38,6) if expression is decimal or numeric with scale != 0
  • money if smallmoney or money used
  • float if the expression is float

You shouldn't be using money or float/real in general, but legacy code is legacy code.

In any case, I expect some calcuations might cause overflows, though I don't know how to fix them if you get one. Maybe break the calculation into two parts? Maybe cast the expression to bigint or decimal(38,6) and hope for the best.

Summary

The PRODUCT() function isn't something I've ever wanted to use, but I do know that there are lots of calculations where this can work well. Especially in financial systems, this is likely to reduce code complexity and make it easy to read queries. This also might prevent some issues when nesting multiple functions that developers or report writers don't quite understand.

If you are involved in this type of work, take a look at the PRODUCT() function and see how it

 

 

    Ad for State of Database Landscape survey

    Rate

    (2)

    Log in orregister to rate

    You rated this post out of 5.Change rating

    Share

    Categories

    Share

    Rate

    (2)

    Log in orregister to rate

    You rated this post out of 5.Change rating

    Related content

    SQLServerCentral Article

    Creating and Using Inline Table-Valued Functions

    Inline Table Valued Functions (iTVFs) are one type of user defined function that is available to implement in SQL Server since SQL Server 2000.

    iTVFs remain a very useful tool in our SQL armoury, so let's quickly revisit them and the different ways we can use them in our code.

      (15)

      Log in orregister to rate

      You rated this post out of 5.Change rating

      2020-10-07(first published:)

      43,948 reads

      SQLServerCentral Article

      Displaying Hierarchical Data

      Producing hierarchies from SQL tables can necessitate joining a table to itself. This article will explain how you can do this.

        (12)

        Log in orregister to rate

        You rated this post out of 5.Change rating

        2020-03-06(first published:)

        24,716 reads

        SQLServerCentral Article

        Count the Number of Weekend Days between Two Dates

        Handling weekends can be tricky in SQL. This article shows you how to Count the Number of Weekend Days between Two Dates

          (9)

          Log in orregister to rate

          You rated this post out of 5.Change rating

          2020-10-16(first published:)

          12,534 reads

          SQLServerCentral Article

          Aggregate Data for the Last Day of the Month

          On some occasions you will need to aggregate Data for the Last Day of the Month. This article explains how.

            (8)

            Log in orregister to rate

            You rated this post out of 5.Change rating

            2020-04-03(first published:)

            6,862 reads

            SQLServerCentral Article

            OUTPUT Clause Basics (For Inserts and Deletes)

            When INSERTING or DELETING rows from a table, the OUTPUT clause can be used to return a dataset containing the changes made. Mat Richardson explains how.

              (28)

              Log in orregister to rate

              You rated this post out of 5.Change rating

              2019-12-27(first published:)

              9,854 reads


              [8]ページ先頭

              ©2009-2025 Movatter.jp