支付两个范围之间的所有数据点

时间:2016-12-09 17:46:54

标签: sql postgresql

我有一个表格,每个州的两个邮政编码之间有几行排列范围。我希望能够获取两个范围并在它们各自的行上显示两个范围之间的每个值。 EX下面。

Zip Start   Zip End     State
00501       06390       NY
10001       10314       NY
10451       11003       NY

我希望将数据显示为

00501    NY
00502    NY
00503    NY
00504    NY

非常感谢任何帮助。

    DROP TABLE IF EXISTS yourTable;
CREATE TABLE yourTable as
Select '00501' as zipstart,'06390' as zipend,'NY' as state union
select'10001','10314','NY'union
select'10451','11003','NY'

Distributed by (ZipStart,ZipEnd,State);

WITH cte AS (
    SELECT *
    FROM
       (VALUES (0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) t(n)
)

, cteTally AS (
    SELECT
       ROW_NUMBER() OVER (ORDER BY (SELECT 1)) as Num
    FROM
       cte c1
       CROSS JOIN cte c2
       CROSS JOIN cte c3
       CROSS JOIN cte c4
       CROSS JOIN cte c5
)

SELECT
    SUBSTRING(ZipStart || CAST(tt.Num AS VARCHAR(100)),1,5) ZipSTart
    ,t.zipend,t.State
FROM
    yourTable t
    INNER JOIN cteTally tt
    ON tt.Num <= CAST(t.ZipEnd AS INT)
    AND tt.Num >= CAST(t.ZipStart AS INT)

3 个答案:

答案 0 :(得分:3)

的PostgreSQL

select  to_char(zip,'FM00000') as zip
       ,state

from    t,generate_series("Zip Start"::int,"Zip End"::int) gs(zip)
;

答案 1 :(得分:1)

这是一个可以在PostgreSQL和其他几个平台上运行的Sql server版本。处理多达100,000个数字以进行扩展只需在cteTally定义中添加更多交叉连接。

IF OBJECT_ID('tempdb..#yourTable') IS NOT NULL
    BEGIN
        DROP TABLE #yourTable
    END

CREATE TABLE #yourTable (ZipStart VARCHAR(5), ZipEnd VARCHAR(5), State CHAR(2));
INSERT INTO #yourTable VALUES
('00501','06390','NY')
,('10001','10314','NY')
,('10451','11003','NY');

WITH cte AS (
    SELECT *
    FROM
       (VALUES (0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) t(n)
)

, cteTally AS (
    SELECT
       ROW_NUMBER() OVER (ORDER BY (SELECT 1)) as Num
    FROM
       cte c1 --10
       CROSS JOIN cte c2 --100
       CROSS JOIN cte c3 --1,000
       CROSS JOIN cte c4 --10,000
       CROSS JOIN cte c5 --100,000
)

SELECT
    RIGHT('00000' || CAST(tt.Num AS VARCHAR(100)),5) as GeneratedZip
    ,t.ZipStart
    ,t.ZipEnd
    ,t.State
FROM
    #yourTable t
    INNER JOIN cteTally tt
    ON tt.Num <= CAST(t.ZipEnd AS INT)
    AND tt.Num >= CAST(t.ZipStart AS INT)

对于PostgreSQL以及支持generate_series的平台,请查看Dudu's answer

答案 2 :(得分:1)

  

这适用于 Sql Server 。我把它留在这里只是因为我花时间写它。

使用数字表非常简单。

rextester:http://rextester.com/MEFE32862

来自@AaronBertrand's article on generating a set的号码表。

begin;
create table dbo.zip_ranges (id int ,StateName varchar(32) ,[State] char(2) ,Zip_Start int ,Zip_End int ) 
  insert into dbo.zip_ranges (id, StateName, [State], Zip_Start, Zip_End) values  
    (1 ,'New York (Fishers Is)' ,'NY' ,501 ,6390) ,(2 ,'New York' ,'NY' ,10001 ,10314),(3 ,'New York' ,'NY' ,10001 ,10314) ,(4 ,'Ohio' ,'OH' ,43001 ,45999) 
  create unique clustered index n on dbo.zip_ranges(Id)
end;
/* 100,000 number table - stolen from @AaronBertrand https://sqlperformance.com/2013/01/t-sql-queries/generate-a-set-1 */  
begin;
  select top (100000) n = convert(int, row_number() over (order by s1.[object_id]))
    into dbo.numbers
    from sys.all_objects as s1 
      cross join sys.all_objects as s2
    option (maxdop 1);
  create unique clustered index n on dbo.numbers(n)
end;

select 
        --Zip=n.n
        --Zip=left('00000',5-len(n.n))+convert(varchar(5),n.n)
        Zip=right('00000'+convert(varchar(5),n.n),5) /* grabbed this conversion from @Matt instead of my clunky one */
      , z.[State]

from dbo.zip_ranges z
    inner join dbo.numbers n on n >= z.zip_start and n <= z.zip_end
    where z.[State]='NY'
    order by n