Need To Improve View Performance

  • I have a SQL database with a number of tables that are loaded from an old flat-file system. the process for loading one of these is now unable to cope with the volume of data so, as a short-term fix, I need to replace the table with a view composed of data from other tables. That way, applications can use the view instead of the table. My replacement data consists of an Addressee table and an AddressBlock table. The AddressBlock is a set of address lines uniquely identified by an AddressCode. The Addressee links a client to one or many addresses (eg. home address, work address, etc.). Addresses may be shared by multiple clients. Additionally, the Addressee table contains Title lines that are combined with the address lines to form the final output.

    I'm running SQL Server 2008 R2.

    1) Create a database to play in.

    IF EXISTS #SELECT name FROM sys.databases WHERE name = N'DemoDB'#

    drop DATABASE [DemoDB];

    go

    CREATE DATABASE [DemoDB];

    go

    2) Create the tables.

    Use DemoDB;

    go

    IF OBJECT_ID('dbo.Addressee','U') IS NOT NULL

    DROP TABLE dbo.Addressee;

    IF OBJECT_ID('dbo.AddressBlock','U') IS NOT NULL

    DROP TABLE dbo.AddressBlock;

    create table dbo.Addressee(

    [AddresseeID] [bigint] identity(1,1) not null,

    [AddressCode] [char](10) not null,

    [Client] [char](5) not null,

    [UsageNum] [tinyint] not null,

    [Title_1] [varchar](40) not null,

    [Title_2] [varchar](40) not null,

    [Title_3] [varchar](40) not null,

    constraint [PK_Addressee] primary key clustered

    (

    [AddresseeID] asc

    )with (pad_index = off, statistics_norecompute = off, ignore_dup_key = off, allow_row_locks = on, allow_page_locks = on) on [PRIMARY]

    ) on [PRIMARY];

    create table dbo.AddressBlock(

    [AddressCode] [char](10) not null,

    [PostCode] [char](8) not null,

    [AddressLine1] [varchar](40) not null,

    [AddressLine2] [varchar](40) not null,

    [AddressLine3] [varchar](40) not null,

    [AddressLine4] [varchar](40) not null,

    [AddressLine5] [varchar](40) not null,

    [AddressLine6] [varchar](40) not null,

    [AddressLine7] [varchar](40) not null,

    [AddressLine8] [varchar](40) not null,

    constraint [PK_AddressBlock] primary key clustered

    (

    [AddressCode] asc

    )with (pad_index = off, statistics_norecompute = off, ignore_dup_key = off, allow_row_locks = on, allow_page_locks = on, fillfactor = 90) on [PRIMARY]

    ) on [PRIMARY];

    go

    3) Populate with data.

    insert dbo.Addressee

    (AddressCode,

    Client,

    UsageNum,

    Title_1,

    Title_2,

    Title_3)

    select'ZYADDR1', 'ZY001', 14, 'Marjorie Dibbs', 'ZY Holdings Ltd.', '' union all

    select'ZYADDR1', 'ZY001', 1, 'ZY Holdings Ltd.', '', '' union all

    select'ZYADDR1', 'ZY001', 15, 'Marjorie Dibbs', 'CEO', 'ZY Holdings Ltd.' union all

    select'ZYADDR1', 'ZY002', 1, 'ZY Partners LLP', '', '' union all

    select'ZYADDR1', 'ZY002', 14, 'Stuart Dibbs', 'ZY Partners LLP', '' union all

    select'DIBBADDR', 'ZY002', 13, 'Mr. S. Dibbs', '', '' union all

    select'DIBBADDR', 'ZY001', 13, 'Mrs. Marjorie Dibbs', '', '';

    insert dbo.AddressBlock

    (AddressCode,

    PostCode,

    AddressLine1,

    AddressLine2,

    AddressLine3,

    AddressLine4,

    AddressLine5,

    AddressLine6,

    AddressLine7,

    AddressLine8)

    select'ZYADDR1', 'BB01 1AB', '4th Floor', 'Office Block', 'Sloane Square', 'London', '', '', '', '' union all

    select'ZYADDR2', 'BB01 1AB', '2nd Floor', 'Office Block', 'Sloane Square', 'London', '', '', '', '' union all

    select'DIBBADDR', 'AA01 1DI', 'Rose Cottage', '1 High St.', 'Lower Pagwell', 'Gloucester', 'Gloucestershire', '', '', '';

    4) My solution uses a function based on the DelimitedSplit8k and a view to provide the formatted output (note: the actual function comments include all the credits from DelimitedSplit8k, but I left them out here for the sake of brevity).

    create function [dbo].[ufn_SplitAddress]

    /* Based on the famed DelimitedSplit8k

    Rev 10 - 21 Apr 2015 - Chris Wooding

    Stripped down a bit to handle up to 500 characters and up to 12 elements for use in

    splitting addresses. Also return the address lines as columns in one row instead of

    multiple rows.

    **********************************************************************************************************************/

    -- Define I/O parameters

    (@pString varchar(500), @pDelimiter char(1))

    returns table with schemabinding as

    return

    -- "Inline" CTE Driven "Tally Table" produces values from 0 up to 100

    with E1(N) as (

    select 1 union all select 1 union all select 1 union all

    select 1 union all select 1 union all select 1 union all

    select 1 union all select 1 union all select 1 union all select 1

    ), --10E+1 or 10 rows

    E2(N) as (select 1 from E1 a, E1 b), --10E+2 or 100 rows

    cteTally(N) as (-- This provides the "base" CTE and limits the number of rows right up front

    -- for both a performance gain and prevention of accidental "overruns"

    select top (isnull(datalength(@pString),0)) row_number() over (order by (select null)) from E2

    ),

    cteStart(N1) as (-- This returns N+1 (starting position of each "element" just once for each delimiter)

    select 1 union all

    select t.N+1 from cteTally t where substring(@pString,t.N,1) = @pDelimiter

    ),

    cteLen(N1,L1) as(-- Return start and length (for use in substring)

    select s.N1,

    isnull(nullif(charindex(@pDelimiter,@pString,s.N1),0)-s.N1,500)

    -- The isnull/NULLIF combo handles the length for the final element when no delimiter is found.

    from cteStart s

    )

    -- Do the actual split and pivot rows into columns.

    select [1], [2], [3], [4], [5], [6],

    [7], [8], [9], [10], [11], [12]

    from (select substring(@pString, l.N1, l.L1) as AddressLine, row_number() over(order by l.N1) as ItemNumber

    from cteLen l) as AddressLines

    pivot (max(AddressLine)

    for ItemNumber in ([1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12])) as AddressRows

    ;

    go

    create view [dbo].[FormattedAddress] as

    with CompressedAddress as (

    selectdistinct

    a.AddressCode, ab.PostCode,

    case

    when a.Title_1 > '' then

    replace(replace(replace(

    a.Title_1 + '|' +

    a.Title_2 + '|' +

    ab.AddressLine1 + '|' +

    ab.AddressLine2 + '|' +

    ab.AddressLine3 + '|' +

    ab.AddressLine4 + '|' +

    ab.AddressLine5 + '|' +

    ab.AddressLine6 + '|' +

    ab.AddressLine7 + '|' +

    ab.AddressLine8 + '|' +

    -- Ensure the 2 postcode parts are separated by only a single space

    replace(replace(replace(rtrim(ab.PostCode), ' ', '{}'), '}{', ''), '{}', ' ')

    -- Remove consecutive delimiters

    , '|', '{}'), '}{', ''), '{}', '|')

    else

    replace(replace(replace(

    ab.AddressLine1 + '|' +

    ab.AddressLine2 + '|' +

    ab.AddressLine3 + '|' +

    ab.AddressLine4 + '|' +

    ab.AddressLine5 + '|' +

    ab.AddressLine6 + '|' +

    ab.AddressLine7 + '|' +

    ab.AddressLine8 + '|' +

    replace(replace(replace(rtrim(ab.PostCode), ' ', '{}'), '}{', ''), '{}', ' ')

    , '|', '{}'), '}{', ''), '{}', '|')

    end as AddressLines

    from dbo.Addressee a

    inner join dbo.AddressBlock ab on a.AddressCode = ab.AddressCode),

    SplitAddress as (

    select ca.AddressCode, ca.PostCode, sa.*

    from CompressedAddress ca

    cross apply dbo.ufn_SplitAddress(ca.AddressLines, '|') sa)

    selectAddressCode,

    row_number() over (partition by AddressCode order by AddressCode) as AddressInstance,

    PostCode,

    isnull(s.[1], '') as AddressLine1,

    isnull(s.[2], '') as AddressLine2,

    isnull(s.[3], '') as AddressLine3,

    isnull(s.[4], '') as AddressLine4,

    isnull(s.[5], '') as AddressLine5,

    isnull(s.[6], '') as AddressLine6,

    isnull(s.[7], '') as AddressLine7,

    isnull(s.[8], '') as AddressLine8

    from SplitAddress s

    go

    5) The expected output is shown in the attached spreadsheet. My solution gives this, but is too slow over the 180k AddressBlock and 530k Address rows in our database. I'm hoping someone here can see a way to speed it up.

    Thanks

  • Hi Chris

    here is an alternative method....works on your sample data....probably need a bit of tidy up

    WITH cte as (

    SELECT

    ROW_NUMBER() OVER (order by (select null) ) - ROW_NUMBER() OVER (partition by AddresseeID order by A.AddressCode) AS rn2

    ,A.AddressCode

    ,AddLine

    ,addvalue

    FROM Addressee AS A INNER JOIN

    AddressBlock AS AB ON A.AddressCode = AB.AddressCode

    CROSS APPLY

    ( values

    ('add1', title_1),

    ('add2', title_2),

    ('add3', AddressLine1),

    ('add4', AddressLine2),

    ('add5', AddressLine3),

    ('add6', AddressLine4),

    ('add7', AddressLine5),

    ('add8', AddressLine6),

    ('add9', Postcode)

    ) c (AddLine,addvalue)

    )

    , cte2 as (

    SELECT

    rn2

    , ROW_NUMBER() OVER (PARTITION BY rn2 ORDER BY (SELECT NULL) ) AS rn3

    , AddressCode

    , AddLine

    , addvalue

    FROM cte

    WHERE addvalue > ''

    )

    SELECT

    AddressCode

    , max (case when rn3 = 1 then addvalue else null end) add1

    , max (case when rn3 = 2 then addvalue else null end) add2

    , max (case when rn3 = 3 then addvalue else null end) add3

    , max (case when rn3 = 4 then addvalue else null end) add4

    , max (case when rn3 = 5 then addvalue else null end) add5

    , max (case when rn3 = 6 then addvalue else null end) add6

    , max (case when rn3 = 7 then addvalue else null end) add7

    FROM cte2

    GROUP BY AddressCode, rn2

    ORDER BY AddressCode

    , max (case when rn3 = 1 then addvalue else null end)

    , max (case when rn3 = 2 then addvalue else null end)

    ________________________________________________________________
    you can lead a user to data....but you cannot make them think
    and remember....every day is a school day

  • This is what I came up with:

    SELECT

    ab.AddressCode,

    AddressInstance = ROW_NUMBER() OVER (Partition by ab.AddressCode ORDER BY (SELECT (NULL))),

    ab.PostCode,

    AddressLine1 = title_1,

    AddressLine2 = CASE WHEN Title_2 = '' THEN ab.AddressLine1 ELSE Title_2 END,

    AddressLine3 = CASE WHEN Title_2 = '' THEN ab.AddressLine2 ELSE ab.AddressLine1 END,

    AddressLine4 = CASE WHEN Title_2 = '' THEN ab.AddressLine3 ELSE ab.AddressLine2 END,

    AddressLine5 = CASE WHEN Title_2 = '' THEN ab.AddressLine4 ELSE ab.AddressLine3 END,

    AddressLine6 = CASE WHEN Title_2 = '' THEN ISNULL(NULLIF(ab.AddressLine5,''),ab.PostCode) ELSE ab.AddressLine4 END,

    AddressLine7 =

    CASE

    WHEN

    (Title_2 <> '' AND ab.AddressLine5 = '') OR

    (Title_2 = '' AND ab.AddressLine5 <> '')

    THEN ab.PostCode

    ELSE ''

    END,

    AddressLine8 = '' -- don't understand a scenario where this would not be blank

    FROM dbo.AddressBlock ab

    JOIN dbo.Addressee a ON a.AddressCode = ab.AddressCode

    You might need to test my solution with more data but this works with the data provided.

    Furthermore, you can increase the performance of what I posted by pretty dramatically by adding this index:

    CREATE NONCLUSTERED INDEX nc_addressee_addresscode ON dbo.Addressee (AddressCode)

    INCLUDE (Title_1, Title_2);

    "I cant stress enough the importance of switching from a sequential files mindset to set-based thinking. After you make the switch, you can spend your time tuning and optimizing your queries instead of maintaining lengthy, poor-performing code."

    -- Itzik Ben-Gan 2001

  • Alan.B (4/22/2015)


    This is what I came up with:

    SELECT

    ab.AddressCode,

    AddressInstance = ROW_NUMBER() OVER (Partition by ab.AddressCode ORDER BY (SELECT (NULL))),

    ab.PostCode,

    AddressLine1 = title_1,

    AddressLine2 = CASE WHEN Title_2 = '' THEN ab.AddressLine1 ELSE Title_2 END,

    AddressLine3 = CASE WHEN Title_2 = '' THEN ab.AddressLine2 ELSE ab.AddressLine1 END,

    AddressLine4 = CASE WHEN Title_2 = '' THEN ab.AddressLine3 ELSE ab.AddressLine2 END,

    AddressLine5 = CASE WHEN Title_2 = '' THEN ab.AddressLine4 ELSE ab.AddressLine3 END,

    AddressLine6 = CASE WHEN Title_2 = '' THEN ISNULL(NULLIF(ab.AddressLine5,''),ab.PostCode) ELSE ab.AddressLine4 END,

    AddressLine7 =

    CASE

    WHEN

    (Title_2 <> '' AND ab.AddressLine5 = '') OR

    (Title_2 = '' AND ab.AddressLine5 <> '')

    THEN ab.PostCode

    ELSE ''

    END,

    AddressLine8 = '' -- don't understand a scenario where this would not be blank

    FROM dbo.AddressBlock ab

    JOIN dbo.Addressee a ON a.AddressCode = ab.AddressCode

    You might need to test my solution with more data but this works with the data provided.

    Furthermore, you can increase the performance of what I posted by pretty dramatically by adding this index:

    CREATE NONCLUSTERED INDEX nc_addressee_addresscode ON dbo.Addressee (AddressCode)

    INCLUDE (Title_1, Title_2);

    Alan...this is very similar to what I was originally going to post.....but I dont think it deals correctly if an address line in blank/NULL

    code to test

    IF OBJECT_ID('dbo.Addressee','U') IS NOT NULL

    DROP TABLE dbo.Addressee;

    IF OBJECT_ID('dbo.AddressBlock','U') IS NOT NULL

    DROP TABLE dbo.AddressBlock;

    create table dbo.Addressee(

    [AddresseeID] [bigint] identity(1,1) not null,

    [AddressCode] [char](10) not null,

    [Client] [char](5) not null,

    [UsageNum] [tinyint] not null,

    [Title_1] [varchar](40) not null,

    [Title_2] [varchar](40) not null,

    [Title_3] [varchar](40) not null,

    constraint [PK_Addressee] primary key clustered

    (

    [AddresseeID] asc

    )with (pad_index = off, statistics_norecompute = off, ignore_dup_key = off, allow_row_locks = on, allow_page_locks = on) on [PRIMARY]

    ) on [PRIMARY];

    create table dbo.AddressBlock(

    [AddressCode] [char](10) not null,

    [PostCode] [char](8) not null,

    [AddressLine1] [varchar](40) not null,

    [AddressLine2] [varchar](40) not null,

    [AddressLine3] [varchar](40) not null,

    [AddressLine4] [varchar](40) not null,

    [AddressLine5] [varchar](40) not null,

    [AddressLine6] [varchar](40) not null,

    [AddressLine7] [varchar](40) not null,

    [AddressLine8] [varchar](40) not null,

    constraint [PK_AddressBlock] primary key clustered

    (

    [AddressCode] asc

    )with (pad_index = off, statistics_norecompute = off, ignore_dup_key = off, allow_row_locks = on, allow_page_locks = on, fillfactor = 90) on [PRIMARY]

    ) on [PRIMARY];

    go

    insert dbo.Addressee

    (AddressCode,

    Client,

    UsageNum,

    Title_1,

    Title_2,

    Title_3)

    select'ZYADDR1', 'ZY001', 14, 'Marjorie Dibbs', 'ZY Holdings Ltd.', '' union all

    select'ZYADDR1', 'ZY001', 1, 'ZY Holdings Ltd.', '', '' union all

    select'ZYADDR1', 'ZY001', 15, 'Marjorie Dibbs', 'CEO', 'ZY Holdings Ltd.' union all

    select'ZYADDR1', 'ZY002', 1, 'ZY Partners LLP', '', '' union all

    select'ZYADDR1', 'ZY002', 14, 'Stuart Dibbs', 'ZY Partners LLP', '' union all

    select'DIBBADDR', 'ZY002', 13, 'Mr. S. Dibbs', '', '' union all

    select'DIBBADDR', 'ZY001', 13, 'Mrs. Marjorie Dibbs', '', '';

    insert dbo.AddressBlock

    (AddressCode,

    PostCode,

    AddressLine1,

    AddressLine2,

    AddressLine3,

    AddressLine4,

    AddressLine5,

    AddressLine6,

    AddressLine7,

    AddressLine8)

    select'ZYADDR1', 'BB01 1AB', '4th Floor', '', 'Sloane Square', 'London', '', '', '', '' union all

    select'ZYADDR2', 'BB01 1AB', '2nd Floor', 'Office Block', '', 'London', '', '', '', '' union all

    select'DIBBADDR', 'AA01 1DI', 'Rose Cottage', '1 High St.', 'Lower Pagwell', 'Gloucester', 'Gloucestershire', '', '', '';

    ________________________________________________________________
    you can lead a user to data....but you cannot make them think
    and remember....every day is a school day

  • J Livingston SQL (4/22/2015)


    Alan.B (4/22/2015)


    This is what I came up with:

    SELECT

    ab.AddressCode,

    AddressInstance = ROW_NUMBER() OVER (Partition by ab.AddressCode ORDER BY (SELECT (NULL))),

    ab.PostCode,

    AddressLine1 = title_1,

    AddressLine2 = CASE WHEN Title_2 = '' THEN ab.AddressLine1 ELSE Title_2 END,

    AddressLine3 = CASE WHEN Title_2 = '' THEN ab.AddressLine2 ELSE ab.AddressLine1 END,

    AddressLine4 = CASE WHEN Title_2 = '' THEN ab.AddressLine3 ELSE ab.AddressLine2 END,

    AddressLine5 = CASE WHEN Title_2 = '' THEN ab.AddressLine4 ELSE ab.AddressLine3 END,

    AddressLine6 = CASE WHEN Title_2 = '' THEN ISNULL(NULLIF(ab.AddressLine5,''),ab.PostCode) ELSE ab.AddressLine4 END,

    AddressLine7 =

    CASE

    WHEN

    (Title_2 <> '' AND ab.AddressLine5 = '') OR

    (Title_2 = '' AND ab.AddressLine5 <> '')

    THEN ab.PostCode

    ELSE ''

    END,

    AddressLine8 = '' -- don't understand a scenario where this would not be blank

    FROM dbo.AddressBlock ab

    JOIN dbo.Addressee a ON a.AddressCode = ab.AddressCode

    You might need to test my solution with more data but this works with the data provided.

    Furthermore, you can increase the performance of what I posted by pretty dramatically by adding this index:

    CREATE NONCLUSTERED INDEX nc_addressee_addresscode ON dbo.Addressee (AddressCode)

    INCLUDE (Title_1, Title_2);

    Alan...this is very similar to what I was originally going to post.....but I dont think it deals correctly if an address line in blank/NULL

    code to test

    IF OBJECT_ID('dbo.Addressee','U') IS NOT NULL

    DROP TABLE dbo.Addressee;

    IF OBJECT_ID('dbo.AddressBlock','U') IS NOT NULL

    DROP TABLE dbo.AddressBlock;

    create table dbo.Addressee(

    [AddresseeID] [bigint] identity(1,1) not null,

    [AddressCode] [char](10) not null,

    [Client] [char](5) not null,

    [UsageNum] [tinyint] not null,

    [Title_1] [varchar](40) not null,

    [Title_2] [varchar](40) not null,

    [Title_3] [varchar](40) not null,

    constraint [PK_Addressee] primary key clustered

    (

    [AddresseeID] asc

    )with (pad_index = off, statistics_norecompute = off, ignore_dup_key = off, allow_row_locks = on, allow_page_locks = on) on [PRIMARY]

    ) on [PRIMARY];

    create table dbo.AddressBlock(

    [AddressCode] [char](10) not null,

    [PostCode] [char](8) not null,

    [AddressLine1] [varchar](40) not null,

    [AddressLine2] [varchar](40) not null,

    [AddressLine3] [varchar](40) not null,

    [AddressLine4] [varchar](40) not null,

    [AddressLine5] [varchar](40) not null,

    [AddressLine6] [varchar](40) not null,

    [AddressLine7] [varchar](40) not null,

    [AddressLine8] [varchar](40) not null,

    constraint [PK_AddressBlock] primary key clustered

    (

    [AddressCode] asc

    )with (pad_index = off, statistics_norecompute = off, ignore_dup_key = off, allow_row_locks = on, allow_page_locks = on, fillfactor = 90) on [PRIMARY]

    ) on [PRIMARY];

    go

    insert dbo.Addressee

    (AddressCode,

    Client,

    UsageNum,

    Title_1,

    Title_2,

    Title_3)

    select'ZYADDR1', 'ZY001', 14, 'Marjorie Dibbs', 'ZY Holdings Ltd.', '' union all

    select'ZYADDR1', 'ZY001', 1, 'ZY Holdings Ltd.', '', '' union all

    select'ZYADDR1', 'ZY001', 15, 'Marjorie Dibbs', 'CEO', 'ZY Holdings Ltd.' union all

    select'ZYADDR1', 'ZY002', 1, 'ZY Partners LLP', '', '' union all

    select'ZYADDR1', 'ZY002', 14, 'Stuart Dibbs', 'ZY Partners LLP', '' union all

    select'DIBBADDR', 'ZY002', 13, 'Mr. S. Dibbs', '', '' union all

    select'DIBBADDR', 'ZY001', 13, 'Mrs. Marjorie Dibbs', '', '';

    insert dbo.AddressBlock

    (AddressCode,

    PostCode,

    AddressLine1,

    AddressLine2,

    AddressLine3,

    AddressLine4,

    AddressLine5,

    AddressLine6,

    AddressLine7,

    AddressLine8)

    select'ZYADDR1', 'BB01 1AB', '4th Floor', '', 'Sloane Square', 'London', '', '', '', '' union all

    select'ZYADDR2', 'BB01 1AB', '2nd Floor', 'Office Block', '', 'London', '', '', '', '' union all

    select'DIBBADDR', 'AA01 1DI', 'Rose Cottage', '1 High St.', 'Lower Pagwell', 'Gloucester', 'Gloucestershire', '', '', '';

    I agree about the Blank/NULL situation. I should have been more clear that what I posted was not a final solution. I do think it could be tweaked to handle blanks/nulls I just did not have enough time.

    My main point (that I did not articulate) was that concatenating then splitting the address in the way that the OP did it was certainly not necessary. Your solution demonstrates this as well.

    "I cant stress enough the importance of switching from a sequential files mindset to set-based thinking. After you make the switch, you can spend your time tuning and optimizing your queries instead of maintaining lengthy, poor-performing code."

    -- Itzik Ben-Gan 2001

  • Thanks for your responses to this.

    J Livingstone, I got an error message trying to create a view with your code; "The ORDER BY clause is invalid in views, inline functions, etc. etc.". I need this to be a view because the intention is to remove the existing table and give the view the name of the table so that calling applications don't see a change.

    Alan, I am getting quite a few duplicate lines with your solution (but it is much faster than mine).

    I had trouble creating the original post and there are some clarifications missing. The AddressBlock table has up to 8 lines, but in practice the last two are always blank. The 3 Title lines may be present or blank, but will always be populated in order (the same applies to the Address lines with the additional condition that at least 1 line will always be present). In the final output, I need the 8 lines to be distinct. The AddressInstance column simply indicates how many times an AddressCode is used and the order in which it is incremented is not important (in fact, rather than incrementing, it could be replaced by a simple count because the only time an application uses it, it selects the MAX value for an AddressCode).

    Chris

  • Update: I tried the J Livingstone solution as a straight SELECT rather than a view definition. It takes about the same time as my solution and has the same duplicates problem as Alan's.

    Further data clarification; a client can link to an address multiple times with different values of UsageNum. Often there will be different Title lines for each, but sometimes this is not the case. I think this is the cause of the duplications in the posted solutions. If you use the replacement Addressee data below, it illustrates this situation (the second row is the new one). The final results should still match those in my original spreadsheet.

    insert dbo.Addressee

    (AddressCode,

    Client,

    UsageNum,

    Title_1,

    Title_2,

    Title_3)

    select'ZYADDR1', 'ZY001', 14, 'Marjorie Dibbs', 'ZY Holdings Ltd.', '' union all

    select'ZYADDR1', 'ZY001', 3, 'Marjorie Dibbs', 'ZY Holdings Ltd.', '' union all

    select'ZYADDR1', 'ZY001', 1, 'ZY Holdings Ltd.', '', '' union all

    select'ZYADDR1', 'ZY001', 15, 'Marjorie Dibbs', 'CEO', 'ZY Holdings Ltd.' union all

    select'ZYADDR1', 'ZY002', 1, 'ZY Partners LLP', '', '' union all

    select'ZYADDR1', 'ZY002', 14, 'Stuart Dibbs', 'ZY Partners LLP', '' union all

    select'DIBBADDR', 'ZY002', 13, 'Mr. S. Dibbs', '', '' union all

    select'DIBBADDR', 'ZY001', 13, 'Mrs. Marjorie Dibbs', '', '';

Viewing 7 posts - 1 through 6 (of 6 total)

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