I need to generate a list of addresses to which my client is going to send merchandise catalogs. They want to send a certain number of catalogs (X) to a particular zip code (Y) [This has to do with average income in those zip codes]
I have 2 tables in SQL Server 2008 R2: CatalogRequests
and AddressList
CatalogRequests
is a list of how many catalogs we should send to each zip code in our customer database:
| Zip | QuantityRequested |
-------------------------------
| 12345 | 150 |
| 13445 | 800 |
| 45678 | 200 |
| 41871 | 350 |
| 77777 | 125 |
AddressList
is just that, a list of addresses =)
| Name | Address1 | Address2 | City | State | Zip |
---------------------------------------------------------------------------
| Bruce | 1 BatCave Dr | | Gotham City | IL | 12345 |
| Clark | 9 Smallville St | Apt A | Metropolis | NY | 45678 |
| Thor | 5 Valhalla Way | | Asgard | ?? | 77777 |
I racked my brain for a little while trying to do this with an SQL query, then gave up and wrote a small C# program to do what I needed to do (basically, generating a bunch of SQL queries - one for each record in CatalogRequests
).
My question is, how could I have done this with one SQL query? I'm just curious at this point, it seems like there is a way to do it and I'm just missing something. Or might not be possible and I'm crazy =)
The result set would be records from AddressList
that met the requirements in CatalogRequest
(for example, 150 records with a zip code of 12345, 800 records with a zip code of 13445, etc).
Selecting a top n records for each category from any table, can be done easily using row_number function which generates a sequential integer to each row within a partition of a result set.
Syntax - SELECT column1,column2, …, columnN FROM table_name; column1,column2 – Specifies the name of the columns used to fetch. table_name - Specifies the name of the table.
To count the number of rows, use the id column which stores unique values (in our example we use COUNT(id) ). Next, use the GROUP BY clause to group records according to columns (the GROUP BY category above). After using GROUP BY to filter records with aggregate functions like COUNT, use the HAVING clause.
Hmm... What about something like this:
;with addressListWithID
AS
(
SELECT name, address1, adress2, city, state, zip,
ROW_NUMBER() OVER(partition by zip order by newid()) as Row
FROM AddressList
)
SELECT A.name, A.address1, A.adress2, A.city, A.state, A.zip
FROM addressListWithID A
INNER JOIN CatalogRequests C
ON C.zip = A.zip
AND A.row <= C.QuantityRequested
Random addresses in quantity requested.
Untested, because there is no DDL and no test data:
SELECT A.name ,
A.address1 ,
A.adress2 ,
A.city ,
A.[state] ,
A.zip
FROM CatalogRequests AS C
CROSS APPLY ( SELECT TOP ( C.QuantityRequested )
A.name ,
A.address1 ,
A.adress2 ,
A.city ,
A.[state] ,
A.zip
FROM addressListWithID AS A
WHERE C.zip = A.zip
) AS A
One method is to use dynamic SQL (which is dangerous and not to be taken lightly):
DECLARE @SQL varchar(max)
SET @SQL = 'USE Database'
SELECT @SQL = @SQL + 'SELECT TOP '
+ QuantityRequired
+ ' * FROM AddressList WHERE Zip = '
+ Zip
+ ' UNION ALL'
FROM CatalogRequests
SET @SQL = LEFT(@SQL, (LEN(@SQL - 10))) -- remove last UNION ALL
PRINT (@SQL)
-- EXEC (@SQL)
Check the link for the canonical reference on Dynamic SQL.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With