programming within SELECT statement

  • Hi,

    Does SQL Server allow programming within a SELECT statement?

    For instance, SELECT col1, col2 (some programming result) as Col3 FROM tables

    Could you please share some thoughts.

    thank you

  • etl2016 (3/4/2016)


    Hi,

    Does SQL Server allow programming within a SELECT statement?

    For instance, SELECT col1, col2 (some programming result) as Col3 FROM tables

    Could you please share some thoughts.

    thank you

    Yes.

    Select Col3 = Col1 + Col2

    for example.

    The absence of evidence is not evidence of absence
    - Martin Rees
    The absence of consumable DDL, sample data and desired results is, however, evidence of the absence of my response
    - Phil Parkin

  • etl2016 (3/4/2016)


    Hi,

    Does SQL Server allow programming within a SELECT statement?

    For instance, SELECT col1, col2 (some programming result) as Col3 FROM tables

    Could you please share some thoughts.

    thank you

    What kind of programming?

    Including expressions as Phil did? That won't change the value of the column in the table, it will only affect the result set.

    Luis C.
    General Disclaimer:
    Are you seriously taking the advice and code from someone from the internet without testing it? Do you at least understand it? Or can it easily kill your server?

    How to post data/code on a forum to get the best help: Option 1 / Option 2
  • thanks.

    But, its a bit more programming, involving local-scoped variables, some Queries, some aggregation and a final result.

    Can we say something like, SELECT col1, col2, (DECLARE local_variable INT; SET @local_variable = SELECT (Query)) as Col3 etc..

    Any T-SQL specific programming constructs to be taken care here, both performance wise and functionality wise .

    thank you very much

  • Absolutely no variable declarations or multiple instructions. All variables should be defined before the query.

    You can have queries inside queries. These are called subqueries and are a great tool. However, if you abuse them, they'll generate performance problems.

    Also, you can have a SELECT to return a result set

    SELECT col1,

    col2,

    col3

    FROM TableA

    Or you can have a SELECT to assign values to variables

    SELECT @val1 = col1,

    @val2 = col2,

    @val3 = col3

    FROM TableA

    But you can't mix them.

    If you're trying to do something specific, share it with us and we'll try to guide you. Read the article in my signature to know how to post better questions.

    Luis C.
    General Disclaimer:
    Are you seriously taking the advice and code from someone from the internet without testing it? Do you at least understand it? Or can it easily kill your server?

    How to post data/code on a forum to get the best help: Option 1 / Option 2
  • etl2016 (3/4/2016)


    thanks.

    But, its a bit more programming, involving local-scoped variables, some Queries, some aggregation and a final result.

    Can we say something like, SELECT col1, col2, (DECLARE local_variable INT; SET @local_variable = SELECT (Query)) as Col3 etc..

    Any T-SQL specific programming constructs to be taken care here, both performance wise and functionality wise .

    thank you very much

    That smacks of RBAR (Row By Agonizing Row) and should be avoided even if you could do it.

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

  • It's also worth noting that using SET vs SELECT when assigning a variable will work similarly but have important differences.

    Here is some code to illustrate:

    --Declare a simple table and add some test data

    DECLARE @test-2 TABLE (ID INT, Name VARCHAR(10))

    INSERT INTO @test-2

    VALUES (1, 'AAA'), (2, 'BBB'), (3, 'CCC'), (4, 'DDD')

    --Declare our variables

    DECLARE @set VARCHAR(10), @select VARCHAR(10)

    --Let's see what the table looks like

    SELECT * FROM @test-2

    --This will fail because it will only accept a single (scalar) value

    BEGIN TRY

    SET @set = (SELECT Name FROM @test-2)

    END TRY

    BEGIN CATCH

    SELECT 'ERROR: A SET with multiple values will not work!'

    END CATCH

    --This will work because it's only one value

    SET @set = (SELECT Name FROM @test-2 WHERE ID = 1)

    --This will work but if you don't specify an exact value it will store the last returned value. In this case DDD.

    SELECT @select = Name FROM @test-2

    SELECT @set

    SELECT @select

    --------------------------------------------------------------------

    --Now let's test what happens when no values are found.

    --Set our variables to an initial value.

    SET @set = 'INIT'

    SET @select = 'INIT'

    --This won't fail but notice how it assigns a NULL to the variable

    SET @set = (SELECT Name FROM @test-2 WHERE ID = 5)

    --This won't fail either but the intial value is perserved.

    SELECT @select = Name FROM @test-2 WHERE ID = 5

    SELECT @set

    SELECT @select


    SELECT quote FROM brain WHERE original = 1
    0 rows returned

  • thank you, I think in my case there is no RBAR

    My query extracts from DW and manufactures a summarised aggregate table, which has 1 day information per record, so, max 31 records as the final result set.

    Say, it has below structure:

    A B C

    a1

    a2

    a3

    a4

    In the above A,B, C are columns and a1, a2, a3.....are unique days in a month.

    Now there is requirement to add a new column D which qualifies each aX, whether it is start of the month, if so what year and what month etc. The unique values a1, a2, a3 .... are not from 1 to 31 always. Instead, they represent sequentially incremented INTs, the smallest INT being dayID representing the inception of business, till today and into future.

    There is a Static reference table which holds info on these INTs along with associated attributes.

    Here is where the programming is needed when I say what I need is: SELECT c1, c2, (some programming with a result) as c3

    Here "some programming" is JOIN related programming. Because, after JOIN-ing, there is some logic and computation to manufacture the last column D in the Aggregate table. There is single field in the ref static table, to derive straight forward.

    Hope you agree there is no adversely influencing RBAR situation here (we are talking about 31 max records)

    One option I am thinking is to generate a CTE on Static Ref table to reduce programming complexity within the above SELECT statement, and keep JOIN light weight between CTE and aggregate table, to generate final column D.

    thank you.

  • etl2016 (3/4/2016)


    thank you, I think in my case there is no RBAR

    My query extracts from DW and manufactures a summarised aggregate table, which has 1 day information per record, so, max 31 records as the final result set.

    Say, it has below structure:

    A B C

    a1

    a2

    a3

    a4

    In the above A,B, C are columns and a1, a2, a3.....are unique days in a month.

    Now there is requirement to add a new column D which qualifies each aX, whether it is start of the month, if so what year and what month etc. The unique values a1, a2, a3 .... are not from 1 to 31 always. Instead, they represent sequentially incremented INTs, the smallest INT being dayID representing the inception of business, till today and into future.

    There is a Static reference table which holds info on these INTs along with associated attributes.

    Here is where the programming is needed when I say what I need is: SELECT c1, c2, (some programming with a result) as c3

    Here "some programming" is JOIN related programming. Because, after JOIN-ing, there is some logic and computation to manufacture the last column D in the Aggregate table. There is single field in the ref static table, to derive straight forward.

    Hope you agree there is no adversely influencing RBAR situation here (we are talking about 31 max records)

    One option I am thinking is to generate a CTE on Static Ref table to reduce programming complexity within the above SELECT statement, and keep JOIN light weight between CTE and aggregate table, to generate final column D.

    thank you.

    We cannot really agree anything based on this description. It's just not detailed enough. If you want help with how to code your solution, you are going to have to provide the level of detail previously requested. That includes sample DDL, data and desired results in a format which can be cut & pasted straight into SSMS.

    The absence of evidence is not evidence of absence
    - Martin Rees
    The absence of consumable DDL, sample data and desired results is, however, evidence of the absence of my response
    - Phil Parkin

  • etl2016 (3/4/2016)


    One option I am thinking is to generate a CTE on Static Ref table to reduce programming complexity within the above SELECT statement, and keep JOIN light weight between CTE and aggregate table, to generate final column D.

    SQL Server will have to perform the same JOIN, doesn't matter if you wrap it in CTE or not. It will only simplify the coding.


    Alex Suprun

  • Hope you agree there is no adversely influencing RBAR situation here (we are talking about 31 max records)

    Like Phil said, with no code, it's impossible to say one way or another. Also, it sounds like you're talking about generating 31 rows. My guess is that the source of those 31 rows is bigger than 31 rows. There could be 10M rows that go into each one of them.

  • etl2016 (3/4/2016)


    Now there is requirement to add a new column D which qualifies each aX, whether it is start of the month, if so what year and what month etc. The unique values a1, a2, a3 .... are not from 1 to 31 always. Instead, they represent sequentially incremented INTs, the smallest INT being dayID representing the inception of business, till today and into future.

    To first address your original question, a lot of "programming" can be done in a SELECT, as long as it is in a single expression. Expressions can be very complex though, includinig subqueries. They can even include user-defined functions but this generally performs so poor that my advice is to not go there unless you really know what you are doing.

    The description above sounds like something that can easily be done using expressions.

    Can you post the tables involved (as CREATE TABLE statements - please incluce all constraints, but please remove extra columns that are not relevant to the question), a few well-chose rows of sample data (as INSERT statements), the output you need to get from that sample data, plus an explanation of how this output logically derives from the sample data? That makes it a lot easier for us to help you!


    Hugo Kornelis, SQL Server/Data Platform MVP (2006-2016)
    Visit my SQL Server blog: https://sqlserverfast.com/blog/
    SQL Server Execution Plan Reference: https://sqlserverfast.com/epr/

  • thanks everyone for sharing your thoughts.

    I implemented what I need.

    Structurally it looks like a RBAR scenario, but, given the situation that, tables being joined T1 and T2 are slim, I believe RBAR is okay.

    T1 is manufactured from DW and always contains 30-odd records only (1 day per month). And T2 contains Reference dates of the business, since its inception.

    I derived my final column D as below:

    (

    SELECT

    (

    CONVERT ( varchar(30), ( SELECT MAX (some_date_field) from REF_TABLE T2 WHERE CLAUSE on T1 and T2), 103 )

    +

    ' - '

    +

    CONVERT ( varchar(30), ( SELECT MAX (some_date_field) from REF_TABLE T2 WHERE CLAUSE on T1 and T2), 103 )

    )

    ) AS Column-D

    thank you

  • etl2016 (3/7/2016)


    thanks everyone for sharing your thoughts.

    I implemented what I need.

    Structurally it looks like a RBAR scenario, but, given the situation that, tables being joined T1 and T2 are slim, I believe RBAR is okay.

    T1 is manufactured from DW and always contains 30-odd records only (1 day per month). And T2 contains Reference dates of the business, since its inception.

    I derived my final column D as below:

    (

    SELECT

    (

    CONVERT ( varchar(30), ( SELECT MAX (some_date_field) from REF_TABLE T2 WHERE CLAUSE on T1 and T2), 103 )

    +

    ' - '

    +

    CONVERT ( varchar(30), ( SELECT MAX (some_date_field) from REF_TABLE T2 WHERE CLAUSE on T1 and T2), 103 )

    )

    ) AS Column-D

    thank you

    Check CROSS APPLY.

    You may find some examples here:

    https://social.msdn.microsoft.com/Forums/sqlserver/en-US/796821b0-ba48-49b4-ba42-1783061d8c41/cross-apply-in-sqlserver?forum=transactsql

    _____________
    Code for TallyGenerator

  • Going deeper with the CROSS APPLY suggestion :

    Consider something like this >

    SELECT SUM(someNumberColumn) / (SELECT SUM(someNumberColumn) FROM Table) AS westCoastSalesAsA%OfTotal

    FROM Table

    WHERE someFilterColumn= 'west coast'

    Could be done with something like ( I didnt totally check my syntax but the idea is )

    SELECT SUM(t1.someNumberColumn) / (t2.totalSales) AS westCoastSalesAsA%OfTotal

    FROM Table t1 CROSS APPLY

    (SELECT SUM(someNumberColumn) as totalSales FROM Table) as t2

    WHERE t1.someFilterColumn= 'west coast'

    It appears cleaner.

    ----------------------------------------------------

Viewing 15 posts - 1 through 15 (of 16 total)

You must be logged in to reply to this topic. Login to reply