CSV
Use CSV for SQL Server bulk imports, operational review, simple ETL flows, and teams that prefer transparent row-oriented files.
Use ProdIPData releases as local, versioned GeoIP intelligence. The core workflow is straightforward: choose a release month, load CSV or Parquet files, preserve the snapshot month, and query by numeric IPv4 prefix.
prefix column representing the first IPv4 address in a /24 block.
CSV is the most universal integration format. Parquet is the better default for analytical processing, notebooks, DuckDB, and columnar warehouse-style workflows.
Use CSV for SQL Server bulk imports, operational review, simple ETL flows, and teams that prefer transparent row-oriented files.
Use Parquet for DuckDB, analytics pipelines, notebooks, fast column scans, and large release-level aggregations.
Use MMDB when the application needs embedded lookup behavior rather than tabular analytics or warehouse-style joins.
Convert the dotted IPv4 address to a numeric integer, then locate the row where the address falls inside the published prefix range.
-- Conceptual lookup model
ip_integer >= prefix
AND ip_integer < prefix + 256
This model works because a /24 row covers 256 addresses. For example, the row starting at 134744064 covers the range 8.8.8.0 through 8.8.8.255.
snapshot_month in every table, index, export, and downstream view.BIGINT for numeric IPv4 values to keep arithmetic clear and portable.prefix, iso, asn, and snapshot_month according to query pattern.SQL Server is a strong fit when the dataset becomes part of existing reporting, reconciliation, or production-enrichment workflows.
CREATE TABLE dbo.ProdIP_Prefix_Attributes
(
prefix BIGINT NOT NULL,
city NVARCHAR(200) NULL,
region_state NVARCHAR(200) NULL,
country NVARCHAR(200) NULL,
iso CHAR(2) NULL,
latitude DECIMAL(9,6) NULL,
longitude DECIMAL(9,6) NULL,
timezone NVARCHAR(128) NULL,
asn NVARCHAR(300) NULL,
company NVARCHAR(300) NULL,
host NVARCHAR(300) NULL,
snapshot_month CHAR(7) NOT NULL
);
CREATE INDEX IX_ProdIP_Prefix_Attributes_Prefix
ON dbo.ProdIP_Prefix_Attributes(prefix);
CREATE INDEX IX_ProdIP_Prefix_Attributes_Snapshot_Iso
ON dbo.ProdIP_Prefix_Attributes(snapshot_month, iso);
If the downloaded CSV is compressed, unzip and decompress it first, then point BULK INSERT to the plain CSV path.
BULK INSERT dbo.ProdIP_Prefix_Attributes
FROM 'D:\SCRAP\Export\CSV\release_2026-03\iso=US\prefix_attributes_2026-03_iso-US_part-0001-of-0001.csv'
WITH
(
FORMAT = 'CSV',
FIRSTROW = 2,
FIELDQUOTE = '"',
CODEPAGE = '65001',
TABLOCK
);
CREATE OR ALTER FUNCTION dbo.IPv4ToBigInt
(
@ip VARCHAR(15)
)
RETURNS BIGINT
AS
BEGIN
DECLARE
@a BIGINT = TRY_CONVERT(BIGINT, PARSENAME(@ip, 4)),
@b BIGINT = TRY_CONVERT(BIGINT, PARSENAME(@ip, 3)),
@c BIGINT = TRY_CONVERT(BIGINT, PARSENAME(@ip, 2)),
@d BIGINT = TRY_CONVERT(BIGINT, PARSENAME(@ip, 1));
IF @a IS NULL OR @b IS NULL OR @c IS NULL OR @d IS NULL
OR @a NOT BETWEEN 0 AND 255
OR @b NOT BETWEEN 0 AND 255
OR @c NOT BETWEEN 0 AND 255
OR @d NOT BETWEEN 0 AND 255
BEGIN
RETURN NULL;
END;
RETURN (@a * 16777216) + (@b * 65536) + (@c * 256) + @d;
END;
GO
DECLARE @ip BIGINT = dbo.IPv4ToBigInt('8.8.8.8');
SELECT TOP (1)
prefix,
city,
region_state,
country,
iso,
latitude,
longitude,
timezone,
asn,
company,
host,
snapshot_month
FROM dbo.ProdIP_Prefix_Attributes
WHERE snapshot_month = '2026-03'
AND @ip >= prefix
AND @ip < prefix + 256
ORDER BY prefix DESC;
SELECT
iso,
country,
COUNT_BIG(*) AS prefix_count,
COUNT_BIG(*) * 256 AS ipv4_count,
COUNT(DISTINCT asn) AS observed_asn_count
FROM dbo.ProdIP_Prefix_Attributes
WHERE snapshot_month = '2026-03'
GROUP BY iso, country
ORDER BY ipv4_count DESC;
DuckDB is excellent for local analysis because it can query Parquet directly without a separate database server or import cycle.
CREATE OR REPLACE VIEW prodip_release AS
SELECT *
FROM read_parquet('D:/SCRAP/Export/PARQUET/release_2026-03/iso=*/prefix_attributes_2026-03_iso-*.parquet');
WITH ip_input AS (
SELECT
CAST(split_part(ip, '.', 1) AS UBIGINT) * 16777216 +
CAST(split_part(ip, '.', 2) AS UBIGINT) * 65536 +
CAST(split_part(ip, '.', 3) AS UBIGINT) * 256 +
CAST(split_part(ip, '.', 4) AS UBIGINT) AS ip_int
FROM (VALUES ('8.8.8.8')) AS v(ip)
)
SELECT
r.prefix,
r.city,
r.region_state,
r.country,
r.iso,
r.latitude,
r.longitude,
r.timezone,
r.asn,
r.company,
r.host,
r.snapshot_month
FROM prodip_release r
CROSS JOIN ip_input q
WHERE q.ip_int >= r.prefix
AND q.ip_int < r.prefix + 256
LIMIT 1;
SELECT
iso,
country,
COUNT(*) AS prefix_count,
COUNT(*) * 256 AS ipv4_count,
COUNT(DISTINCT asn) AS observed_asn_count
FROM prodip_release
GROUP BY iso, country
ORDER BY ipv4_count DESC;
SELECT
asn,
COUNT(*) AS prefix_count,
COUNT(*) * 256 AS ipv4_count,
COUNT(DISTINCT iso) AS country_count
FROM prodip_release
WHERE asn IS NOT NULL
AND asn <> 'n/a'
GROUP BY asn
ORDER BY prefix_count DESC
LIMIT 50;
SELECT *
FROM read_csv_auto(
'D:/SCRAP/Export/CSV/release_2026-03/iso=US/*.csv.gz',
header = true
)
LIMIT 10;
The cleanest integration pattern is to enrich your internal event table with a computed numeric IPv4 column, then join to the release table by range.
SELECT
e.event_id,
e.ip_address,
g.country,
g.iso,
g.city,
g.asn,
g.company,
g.snapshot_month
FROM dbo.Events e
JOIN dbo.ProdIP_Prefix_Attributes g
ON e.ip_integer >= g.prefix
AND e.ip_integer < g.prefix + 256
WHERE g.snapshot_month = '2026-03';