OUTER APPLY

  • I need to be able to pass the output of a function to another function as input, where all functions involved are user-defined in-line table-valued functions. I already posted this on Stack Exchange, where I was unable to find any assistance, so here is a link to the relevant code:

    http://stackoverflow.com/questions/20956777/correct-way-to-pass-table-valued-function-outputs-to-table-valued-functions-in-s

    I am fairly certain OUTER APPLY is the core answer here; there's *clearly* some way in which does *not* do what I need, or I would not get the null output you see in the link, but it seems clear that there should be a way to fool it into working. Does anyone know how to do this?

  • from the page you posted, someone suggested this code

    SELECT *

    FROM

    [dbo].bookPageFromAddress(@address) addresses

    outer apply [dbo].[imageFileFromBookPage](addresses.Book, addresses.Page) foo

    you said it didn't work, but didn't say why, or whether you got an error or just unexpected results.

    details would help here;

    Lowell


    --help us help you! If you post a question, make sure you include a CREATE TABLE... statement and INSERT INTO... statement into that table to give the volunteers here representative data. with your description of the problem, we can provide a tested, verifiable solution to your question! asking the question the right way gets you a tested answer the fastest way possible!

  • I actually incorporated the code Lowell pointed out and edited the original post, although it was easy to miss; I have now instead re-built the entire post to try and be much clearer.

    WARNING: The code below constructs the necessary tables, views, and functions to demonstrate the problem by dropping them first if they exist, so please please please check first to make sure you don't drop anything of your own! The final three SELECTS demonstrate the problem; the final two SELECTS should have identical output, but do not - the first one (of the final two, so the middle of the three) is a three row table of strings, and the final one is a one row table containing only a NULL.

    Note that the OUTER APPLY logic Lowell asked about is fully incorporated, as it was in the original link when I posted it here.

    USE [TOM_GIS]

    GO

    IF OBJECT_ID(N'[dbo].[constant]', N'U') IS NOT NULL

    DROP TABLE [dbo].[constant]

    CREATE TABLE [dbo].[constant]

    (

    ID INT IDENTITY(1,1) PRIMARY KEY CLUSTERED,

    BOOK varchar(5),

    PAGE varchar(5),

    DocID numeric(8, 0)

    )

    INSERT INTO [dbo].[constant]

    VALUES(' 4043',' 125', 576030)

    GO

    IF OBJECT_ID(N'[dbo].[images]', N'U') IS NOT NULL

    DROP TABLE [dbo].[images]

    CREATE TABLE [dbo].[images]

    (

    ID INT IDENTITY(1,1) PRIMARY KEY CLUSTERED,

    DocID numeric(8, 0),

    ImageID numeric(12,0)

    )

    INSERT INTO [dbo].[images] VALUES(576030, 1589666);

    INSERT INTO [dbo].[images] VALUES(576030, 1589667);

    INSERT INTO [dbo].[images] VALUES(576030, 1589668);

    GO

    IF OBJECT_ID(N'[dbo].[addressBookPage]', N'U') IS NOT NULL

    DROP TABLE [dbo].[addressBookPage]

    CREATE TABLE [dbo].[addressBookPage]

    (

    ID INT IDENTITY(1,1) PRIMARY KEY CLUSTERED,

    PARCEL_ADDRESS nvarchar(50),

    BOOK nchar(10),

    PAGE nchar(10),

    )

    INSERT INTO [dbo].[addressBookPage]

    VALUES('155 CENTER STREET','4043', '125')

    GO

    IF OBJECT_ID(N'[dbo].[vw_quindraco]') IS NOT NULL

    DROP VIEW [dbo].[vw_quindraco]

    GO

    CREATE VIEW [dbo].[vw_quindraco]

    AS

    WITH files AS (SELECT RIGHT('00000000' + LTRIM(STR(c.DocID)), 8) AS PathInfo

    ,RIGHT('0000000000' + LTRIM(STR(i.ImageID)), 12) AS FileName

    ,ltrim(c.Book) as Book

    ,ltrim(c.Page) as Page

    FROM [dbo].[constant] AS c INNER JOIN

    [dbo].[images] AS i ON c.DocID = i.DocID)

    SELECT 'Images/' + SUBSTRING(PathInfo, 1, 2) + '/' + SUBSTRING(PathInfo, 3, 2) + '/' + SUBSTRING(PathInfo, 5, 2)

    + '/' + RIGHT(PathInfo, 8) + '/' + FileName + '.tif' AS FullFileName

    ,Book

    ,Page

    FROM files AS files_1

    GO

    IF OBJECT_ID(N'[dbo].[bookAndPageFromAddress]') IS NOT NULL

    DROP FUNCTION [dbo].[bookAndPageFromAddress];

    GO

    CREATE FUNCTION [dbo].[bookAndPageFromAddress] (@address NVARCHAR(max))

    RETURNS TABLE AS RETURN(

    SELECT PARCEL_ADDRESS AS Address, Book, Page

    FROM [dbo].[addressBookPage]

    WHERE PARCEL_ADDRESS like '%' + @address + '%'

    );

    GO

    IF OBJECT_ID(N'[dbo].[imageFileNameFromBookPage]') IS NOT NULL

    DROP FUNCTION [dbo].[imageFileNameFromBookPage];

    GO

    CREATE FUNCTION [dbo].[imageFileNameFromBookPage] (@book nvarchar(max), @page nvarchar(max))

    RETURNS TABLE AS RETURN(

    SELECT i.FullFileName

    FROM [dbo].[vw_quindraco] i

    WHERE i.Book like @book

    AND i.Page like @page

    );

    GO

    IF OBJECT_ID(N'[dbo].[imageFileNameFromAddress]') IS NOT NULL

    DROP FUNCTION [dbo].[imageFileNameFromAddress];

    GO

    CREATE FUNCTION [dbo].[imageFileNameFromAddress] (@address NVARCHAR(max))

    RETURNS TABLE AS RETURN(

    SELECT *

    FROM [dbo].[bookAndPageFromAddress](@address) addresses

    OUTER APPLY [dbo].[imageFileNameFromBookPage](addresses.Book, addresses.Page) foo

    );

    GO

    SELECT Book,Page FROM [dbo].[bookAndPageFromAddress]('155 Center Street');

    SELECT FullFileName FROM [dbo].[imageFileNameFromBookPage]('4043','125');

    SELECT FullFileName FROM [dbo].[imageFileNameFromAddress]('155 Center Street')

  • Your sample code generates errors:

    Msg 208, Level 16, State 1, Procedure imageFileNameFromAddress, Line 5

    Invalid object name 'dbo.imageFileFromBookPage'.

    (1 row(s) affected)

    (3 row(s) affected)

    Msg 208, Level 16, State 1, Line 3

    Invalid object name 'dbo.imageFileNameFromAddress'.

    What's your objective here? I think it's something like "using a couple of variables as input parameters, get some rows & columns out of this bunch of tables using the variables as filters". If this is the case, show what output you expect to see from a set of parameters, and why.

    I think you've overcomplicated this and you've ended up pulling yourself up by your shoelaces and falling over. A single view (or inline function) should be sufficient and will eliminate that embarrassing WTF moment down the line when someone else comes along to maintain your code.

    “Write the query the simplest way. If through testing it becomes clear that the performance is inadequate, consider alternative query forms.” - Gail Shaw

    For fast, accurate and documented assistance in answering your questions, please read this article.
    Understanding and using APPLY, (I) and (II) Paul White
    Hidden RBAR: Triangular Joins / The "Numbers" or "Tally" Table: What it is and how it replaces a loop Jeff Moden

  • If you are still wondering why the last query in your script returns NULL for the fullfilename (or whatever), it's because you've been inconsistent with your datatypes.

    -- Columns BOOK and PAGE in table [dbo].[addressBookPage] are defined as nchar(10)

    -- and VARCHAR(5) in table [dbo].[constant]

    -- look at the values when you bound them in square brackets

    SELECT

    '['+a.Book+']', -- [4043 ]

    '['+a.[Page]+']', -- [125 ]

    foo.*

    FROM [dbo].[addressBookPage] a

    outer APPLY [dbo].[imageFileNameFromBookPage](a.Book, a.[Page]) foo

    WHERE a.PARCEL_ADDRESS like '%' + '155 Center Street' + '%'

    -- now look at FUNCTION [dbo].[imageFileNameFromBookPage] where htey are passed in as VARCHAR(MAX)

    WHERE i.Book LIKE @book

    AND i.Page LIKE @page

    -- LIKE can't match [4043 ] to [4043] without either trimming or using a wildcard

    “Write the query the simplest way. If through testing it becomes clear that the performance is inadequate, consider alternative query forms.” - Gail Shaw

    For fast, accurate and documented assistance in answering your questions, please read this article.
    Understanding and using APPLY, (I) and (II) Paul White
    Hidden RBAR: Triangular Joins / The "Numbers" or "Tally" Table: What it is and how it replaces a loop Jeff Moden

  • ChrisM@Work (1/8/2014)


    Your sample code generates errors:

    Msg 208, Level 16, State 1, Procedure imageFileNameFromAddress, Line 5

    Invalid object name 'dbo.imageFileFromBookPage'.

    (1 row(s) affected)

    (3 row(s) affected)

    Msg 208, Level 16, State 1, Line 3

    Invalid object name 'dbo.imageFileNameFromAddress'.

    You used a mispasted version which I corrected seconds later - you're too fast for me! Current version does not do that.

    What's your objective here?

    Dammit, I suspected stating it only three ways was not enough. How can I be clearer? I will try pseudocode.

    I want to know how to properly write this function in SQL:

    //Note: func1 and func2 return tables as output and take scalars as input

    def compose(func1, func2, arguments_to_func1){

    return_table = new table;

    for row in func1(arguments_to_func1){

    return_table.appendTable(func2(rowToArguments(row)));

    }

    return return_table;

    }

    I think it's something like "using a couple of variables as input parameters, get some rows & columns out of this bunch of tables using the variables as filters".[/quote[

    "Using an arbitrary number of variables as input parameters, compose functions together such that they can be called on each other."

    If this is the case, show what output you expect to see from a set of parameters, and why.

    Input is '155 Center Street'; correct outputs to the three SELECTs follow.

    First SELECT:

    Book Page

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

    4043 125

    Second and Third:

    FullFileName

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

    Images/00/57/60/00576030/000001589666.tif

    Images/00/57/60/00576030/000001589667.tif

    Images/00/57/60/00576030/000001589668.tif

    Incorrect output third actually produces:

    FullFileName

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

    NULL

    I think you've overcomplicated this and you've ended up pulling yourself up by your shoelaces and falling over. A single view (or inline function) should be sufficient and will eliminate that embarrassing WTF moment down the line when someone else comes along to maintain your code.

    I am not familiar with parametrized views, nor do I know how to call them, would be very grateful if you would teach me how - I would prefer a view to a function here. My current solution is a user-defined in-line table-valued function which does not, as I stated, actually work.

  • Is [address] the only filter/parameter you are intending to pass in?

    “Write the query the simplest way. If through testing it becomes clear that the performance is inadequate, consider alternative query forms.” - Gail Shaw

    For fast, accurate and documented assistance in answering your questions, please read this article.
    Understanding and using APPLY, (I) and (II) Paul White
    Hidden RBAR: Triangular Joins / The "Numbers" or "Tally" Table: What it is and how it replaces a loop Jeff Moden

  • Thank you! You've finally sussed it. I went back to my function and modified it to rtrim the select from the table with the mangled data type and it works a treat.

  • quindraco (1/8/2014)


    Thank you! You've finally sussed it. I went back to my function and modified it to rtrim the select from the table with the mangled data type and it works a treat.

    quindraco drives slowly into a garage.

    quindraco: “My car pulls to one side when I’m driving“

    Mechanic: “You’ve got a flat tyre”

    quindraco: “Oh right! I’ll flatten the tyre on the opposite side then. It will even everything out”

    Why not make the data type consistent for [book] and [page] columns (and variables to hold the values)? Fix it properly now and it's likely to save you a whole world of pain in the future.

    “Write the query the simplest way. If through testing it becomes clear that the performance is inadequate, consider alternative query forms.” - Gail Shaw

    For fast, accurate and documented assistance in answering your questions, please read this article.
    Understanding and using APPLY, (I) and (II) Paul White
    Hidden RBAR: Triangular Joins / The "Numbers" or "Tally" Table: What it is and how it replaces a loop Jeff Moden

Viewing 9 posts - 1 through 8 (of 8 total)

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