July 4, 2012 at 8:02 pm
nice information
July 4, 2012 at 8:07 pm
Apology accepted!
Still no joy on hitting the optimal solution though. It seems I'm short by one doublet and one triplet. My current results are:
1. 15 doublets (30 districts)
2. 10 triplets (30 districts)
3. 1 singlet district (48) that already fits the population requirement
4. 5 stray districts
Here are the sets:
-- First the special districts you described
[2,47,58];[3,52];[6,13];[9,26];[35,63];[48];
-- Now for the 15 doublets
[8,25];[36,40];[11,64];[27,62];[28,59];[43,57];[23,32];[31,46];[1,12];[42,65];[10,45];[10,45];
-- And the 10 triplets
[22,49,50];[7,14,24];[15,19,37];[5,56,61];[21,30,41];[4,38,54];[17,18,53];[20,29,39];[16,34,44];
-- Finally, the 5 strays
[33];[51];[55];[60];[66]
So I suppose you'll want to know how I achieved such speed so posting of some code is in order. I'll explain as I go along and there are some comments in the code along the way with further explanation.
First we'll create a new districts table, and populate it with valid doublets, triplets and that one special singlet that fits the population profile.
CREATE TABLE NewDistricts
(n INT, d1 INT NOT NULL, d2 INT NOT NULL, d3 INT NOT NULL, dall VARCHAR(320), [Population] INT, PF INT)
ALTER TABLE NewDistricts
ADD PRIMARY KEY (d1, d2, d3)
GO
SET STATISTICS TIME ON
;WITH Doubles AS (
-- This CTE creates (from the Map) all possible combined districts with their populations
SELECT m.district, neighbor, d1=m.district, n1=neighbor
,[population]=SUM([population])
FROM Map m
INNER JOIN Sweden s
ON m.district = s.district or m.neighbor = s.district
GROUP BY m.district, neighbor),
Combine2and3 ( n, d1, d2, d3, dall, [Population]) AS (
-- This recursive CTE (one execution only of the recursive leg) combines the two/three
-- district combinations
SELECT 2, district, neighbor, 0
,CAST(d1 AS VARCHAR(8000)) + ',' + CAST(n1 AS VARCHAR(8000))
,[population]
FROM Doubles
WHERE population <= 6100
UNION ALL
SELECT 1 + n.n, y.d1, y.d2, y.d3
,CASE WHEN d < n.d1
THEN CAST(d AS VARCHAR(8000))+ ',' + CAST(n.dall AS VARCHAR(8000))
WHEN d > n.d2
THEN CAST(n.dall AS VARCHAR(8000)) + ',' + CAST(d AS VARCHAR(8000))
ELSE CAST(n.d1 AS VARCHAR(8000)) + ',' + CAST(d AS VARCHAR(8000)) + ',' + CAST(n.d2 AS VARCHAR(8000))
END
,n.[population] + s.[population]
FROM Combine2and3 n
INNER JOIN Doubles d ON (d.d1 IN (n.d1, n.d2) or d.n1 IN (n.d1, n.d2))
CROSS APPLY (
SELECT CASE WHEN d.d1 IN (n.d1, n.d2) THEN d.n1 ELSE d.d1 END) x(d)
INNER JOIN Sweden s ON s.district = d
-- To support later steps, we'll keep the districts sorted within the tuple
CROSS APPLY (
SELECT d1=CASE WHEN d < n.d1 THEN d WHEN d > d2 THEN n.d1 ELSE n.d1 END
,d2=CASE WHEN d < n.d1 THEN n.d1 WHEN d > d2 THEN n.d2 ELSE d END
,d3=CASE WHEN d < n.d1 THEN n.d2 WHEN d > d2 THEN d ELSE n.d2 END) y
WHERE n < 3 AND y.d1 <> y.d2 AND y.d2 <> y.d3
),
NewDistricts AS (
-- Finally NewDistricts converts the above to a unique set of (132) doublets and triplets
-- for use later. Up through here, all of these steps together are quite fast.
SELECT n, d1, d2, d3, dall, [population]
FROM (
SELECT n, d1, d2, d3, dall='[' + dall + ']', [population]
,rn=ROW_NUMBER() OVER (PARTITION BY n, d1, d2, d3 ORDER BY (SELECT NULL))
FROM Combine2and3
WHERE [population] BETWEEN 5900 AND 6100) x
WHERE rn=1),
CountByOccurrence AS (
SELECT d, [Count], Doublets, Triplets
FROM (
SELECT d, [Count]=SUM([Count]), Doublets=SUM(Doublets), Triplets=SUM(Triplets)
FROM NewDistricts
CROSS APPLY (VALUES (d1, 1, CASE WHEN n=2 THEN 1 ELSE 0 END, CASE WHEN n=3 THEN 1 ELSE 0 END)
,(d2, 1, CASE WHEN n=2 THEN 1 ELSE 0 END, CASE WHEN n=3 THEN 1 ELSE 0 END)
,(d3, 1, CASE WHEN n=2 THEN 1 ELSE 0 END, CASE WHEN n=3 THEN 1 ELSE 0 END)
) x(d, [Count], Doublets, Triplets)
WHERE d <> 0
GROUP BY d) x),
SpecialDistricts AS (
-- Special districts are those that don't exist in the doublets and triplets (only district=48)
SELECT d1, [population], dall='[' + CAST(d1 AS VARCHAR) + ']'
FROM (
SELECT d1=district FROM Sweden EXCEPT
SELECT d1
FROM (
SELECT d1 FROM NewDistricts
UNION ALL SELECT d2 FROM NewDistricts
UNION ALL SELECT d3 FROM NewDistricts) a
) b
INNER JOIN Sweden ON d1=district)
INSERT INTO dbo.NewDistricts
SELECT n, d1, d2, d3, dall, [population], PF
FROM (
SELECT n, d1, d2, d3, dall, [population]
,PF=c1.[Count] + c2.[Count] + ISNULL(c3.[Count], 0) -- Packing factor
FROM NewDistricts
LEFT JOIN CountByOccurrence c1 ON c1.d=d1
LEFT JOIN CountByOccurrence c2 ON c2.d=d2
LEFT JOIN CountByOccurrence c3 ON c3.d=d3
) a
UNION ALL
SELECT 1, d1, 0, 0, dall, [population], 1
FROM SpecialDistricts
-- Now mark those NewDistricts that contain a district with a single occurrence within the n-Tuple
;WITH CountByOccurrence AS (
SELECT d, [Count], Doublets, Triplets
FROM (
SELECT d, [Count]=SUM([Count]), Doublets=SUM(Doublets), Triplets=SUM(Triplets)
FROM NewDistricts
CROSS APPLY (VALUES (d1, 1, CASE WHEN n=2 THEN 1 ELSE 0 END, CASE WHEN n=3 THEN 1 ELSE 0 END)
,(d2, 1, CASE WHEN n=2 THEN 1 ELSE 0 END, CASE WHEN n=3 THEN 1 ELSE 0 END)
,(d3, 1, CASE WHEN n=2 THEN 1 ELSE 0 END, CASE WHEN n=3 THEN 1 ELSE 0 END)
) x(d, [Count], Doublets, Triplets)
WHERE d <> 0
GROUP BY d) x)
UPDATE nd
SET PF=0
FROM dbo.NewDistricts nd
INNER JOIN (
SELECT n, d1, d2, d3, a=a.[Count], b=ISNULL(b.[Count], 0), c=ISNULL(c.[Count], 0)
FROM NewDistricts
LEFT OUTER JOIN CountByOccurrence a ON a.d=d1
LEFT OUTER JOIN CountByOccurrence b ON b.d=d2
LEFT OUTER JOIN CountByOccurrence c ON c.d=d3
WHERE a.[Count]=1 OR ISNULL(b.[Count], 0)=1 OR ISNULL(c.[Count], 0)=1
) a ON nd.d1 = a.d1 AND nd.d2 = a.d2 AND nd.d3 = nd.d3
SET STATISTICS TIME OFF
SELECT * FROM NewDistricts ORDER BY PF, d1, d2, d3
This should run in about a minute and a half, mostly because of my ham-handed approach to calculating PF (my packing factor). Certainly there's probably a faster way to do this, I'm just not into optimizing that yet as we still have bigger fish to fry.
The packing factor establishes the order that we'll try "packing" our doublets and triplets into the final solution. The final SELECT above lists the NewDistricts in that order. I believe that by careful manipulation of the PF ordering, the optimal solution can be attained.
Now for the really fun part!
To build the final solution code, we start with a preamble that combines our 12 "special districts" that I've marked with a PF=0 into our starting array consisting of these fields:
n=Number of combined districts
np=Number of doublets
nt=Number of triplets
population is self evident
dall=The string of combined singlets, doublets and triplets I posted above.
d1, d2, ... d12=The first 12 districts that must participate and can only do so through the special district combinations
SET STATISTICS TIME ON
;WITH BaseDistricts AS (
-- Create a base of districts to start with. Each of the singlet (1), doublet (4) and triplet (1)
-- NewDistricts contain at least one district that appears in no other doublet or triplet.
SELECT d1=[1], d2=[2], d3=[3], d4=[4], d5=[5], d6=[6]
,d7=[7], d8=[8], d9=[9], d10=[10], d11=[11], d12=[12]
,dall, [population]
FROM (
SELECT d, n1=ROW_NUMBER() OVER (ORDER BY d)
FROM dbo.NewDistricts nd
CROSS APPLY (VALUES (d1), (d2), (d3)) b(d)
WHERE PF = 0 AND d<>0
) a
PIVOT (MAX(d) FOR n1 IN ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12])) pt
CROSS APPLY (
SELECT dall=STUFF((
SELECT ';' + dall FROM dbo.NewDistricts WHERE PF = 0
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'), 1, 1, '')
,[population]=SUM([population])
FROM dbo.NewDistricts
WHERE PF = 0) a
)
SELECT
-- n = Number of combined districts
n=12
-- np = Number of combined doublets
,np=4
-- nt = Number of combined triplets
,nt=1
,[population]=base.[population]
,dall=base.dall
,d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12 -- From Base
FROM BaseDistricts base
SET STATISTICS TIME OFF
Not that I'm particularly proud of the above either. I'm no pivot expert so there's probably a more efficient way to do it. All I can say is that I finally got it working and it ran fast enough that I wasn't inclined to try to optimize it.
Next, we'll attempt to CROSS APPLY each remaining NewDistrict (where PF<>0) in the order established above. Unfortunately, this approach requires trying one at a time:
1) If the CROSS APPLY returns no rows, discard (fail) that new district
2) If the CROSS APPLY returns a row, keep that new district and proceed to test the next ones
This is a slow and arduous process! But it is quite repetitive. And in repetition there is the potential for automation (more on that later).
In the following code, I've captured all the successful CROSS APPLYs into the CTE named CombineNewDistricts and then added a little bit of extra to add in our stray districts.
SET STATISTICS TIME ON
;WITH BaseDistricts AS (
-- Create a base of districts to start with. Each of the singlet (1), doublet (4) and triplet (1)
-- NewDistricts contain at least one district that appears in no other doublet or triplet.
SELECT d1=[1], d2=[2], d3=[3], d4=[4], d5=[5], d6=[6]
,d7=[7], d8=[8], d9=[9], d10=[10], d11=[11], d12=[12]
,dall, [population]
FROM (
SELECT d, n1=ROW_NUMBER() OVER (ORDER BY d)
FROM dbo.NewDistricts nd
CROSS APPLY (VALUES (d1), (d2), (d3)) b(d)
WHERE PF = 0 AND d<>0
) a
PIVOT (MAX(d) FOR n1 IN ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12])) pt
CROSS APPLY (
SELECT dall=STUFF((
SELECT ';' + dall FROM dbo.NewDistricts WHERE PF = 0
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'), 1, 1, '')
,[population]=SUM([population])
FROM dbo.NewDistricts
WHERE PF = 0) a
),
CombineNewDistricts AS (
SELECT
-- n = Number of combined districts
n=12+2+2+2+2+2+2+2+2+2+2+2+3+3+3+3+3+3+3+3
-- np = Number of combined doublets
,np=4+1+1+1+1+1+1+1+1+1+1+1
-- nt = Number of combined triplets
,nt=1+1+1+1+1+1+1+1+1+1
,[population]=base.[population] + a.[population] + b.[population] + c.[population] +
d.[population] + e.[population] + f.[population] + g.[population] + h.[population] +
i.[population] + j.[population] + k.[population] + l.[population] + m.[population] +
n.[population] + o.[population] + p.[population] + q.[population] + r.[population] +
s.[population] + t.[population]
,dall=base.dall + ';' + a.dall + ';' + b.dall + ';' + c.dall + ';' + d.dall + ';' + e.dall + ';' +
f.dall + ';' + g.dall + ';' + h.dall + ';' + i.dall + ';' + j.dall + ';' + k.dall + ';' +
k.dall + ';' + l.dall + ';' + m.dall + ';' + n.dall + ';' + o.dall + ';' + p.dall + ';' +
q.dall + ';' + r.dall + ';' + s.dall + ';' + t.dall
,d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12 -- From Base
,d13, d14 -- From a (doublet)
,d15, d16 -- From b (doublet)
,d17, d18 -- From c (doublet)
,d19, d20 -- From d (doublet)
,d21, d22 -- From e (doublet)
,d23, d24 -- From f (doublet)
,d25, d26 -- From g (doublet)
,d27, d28 -- From h (doublet)
,d29, d30 -- From i (doublet)
,d31, d32 -- From j (doublet)
,d33, d34 -- From k (doublet)
,d35, d36, d37 -- From l (triplet)
,d38, d39, d40 -- From m (triplet)
,d41, d42, d43 -- From n (triplet)
,d44, d45, d46 -- From o (triplet)
,d47, d48, d49 -- From p (triplet)
,d50, d51, d52 -- From q (triplet)
,d53, d54, d55 -- From r (triplet)
,d56, d57, d58 -- From s (triplet)
,d59, d60, d61 -- From t (triplet)
FROM BaseDistricts base
-- Try: [6,55] Fail (no rows returned)
-- Try: [8,25] Success (at least one row returned)
CROSS APPLY (
SELECT d13=d1, d14=d2, dall, [population]
FROM dbo.NewDistricts
WHERE n = 2 AND d1 = 8 AND d2 = 25 AND
d1 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12) AND
d2 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12)) a
-- Try: [8,66] Fail
-- Try: [25,55] Fail
-- Try: [36,40] Success!
CROSS APPLY (
SELECT d15=d1, d16=d2, dall, [population]
FROM dbo.NewDistricts
WHERE n = 2 AND d1 = 36 AND d2 = 40 AND
d1 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14) AND
d2 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14)) b
-- Try: [40,66] Fail
-- Try: [11,64] Success!
CROSS APPLY (
SELECT d17=d1, d18=d2, dall, [population]
FROM dbo.NewDistricts
WHERE n = 2 AND d1 = 11 AND d2 = 64 AND
d1 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16) AND
d2 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16)) c
-- Try: [27,62] Success!
CROSS APPLY (
SELECT d19=d1, d20=d2, dall, [population]
FROM dbo.NewDistricts
WHERE n = 2 AND d1 = 27 AND d2 = 62 AND
d1 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18) AND
d2 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18)) d
-- Try: [28,59] Success!
CROSS APPLY (
SELECT d21=d1, d22=d2, dall, [population]
FROM dbo.NewDistricts
WHERE n = 2 AND d1 = 28 AND d2 = 59 AND
d1 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20) AND
d2 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20)) e
-- Try: [31,36] Fail
-- Try: [31,62] Fail
-- Try: [43,59] Fail
-- Try: [11,63] Fail
-- Try: [27,57] Fail
-- Try: [43,57] Success!
CROSS APPLY (
SELECT d23=d1, d24=d2, dall, [population]
FROM dbo.NewDistricts
WHERE n = 2 AND d1 = 43 AND d2 = 57 AND
d1 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22) AND
d2 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22)) f
-- Try: [1,28] Fail
-- Try: [10,57] Fail
-- Try: [11,65] Fail
-- Try: [23,32] Success!
CROSS APPLY (
SELECT d25=d1, d26=d2, dall, [population]
FROM dbo.NewDistricts
WHERE n = 2 AND d1 = 23 AND d2 = 32 AND
d1 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24) AND
d2 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24)) g
-- Try: [27,46] Fail
-- Try: [31,46] Success!
CROSS APPLY (
SELECT d27=d1, d28=d2, dall, [population]
FROM dbo.NewDistricts
WHERE n = 2 AND d1 = 31 AND d2 = 46 AND
d1 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26) AND
d2 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26)) h
-- Try: [42,64] Fail
-- Try: [63,65] Fail
-- Try: [1,32] Fail
-- Try: [10,46] Fail
-- Try: [12,28] Fail
-- Try: [12,43] Fail
-- Try: [23,42] Fail
-- Try: [32,65] Fail
-- Try: [1,12] Success!
CROSS APPLY (
SELECT d29=d1, d30=d2, dall, [population]
FROM dbo.NewDistricts
WHERE n = 2 AND d1 = 1 AND d2 = 12 AND
d1 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28) AND
d2 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28)) i
-- Try: [10,12] Fail
-- Try: [23,45] Fail
-- Try: [32,42] Fail
-- Try: [42,65] Success!
CROSS APPLY (
SELECT d31=d1, d32=d2, dall, [population]
FROM dbo.NewDistricts
WHERE n = 2 AND d1 = 42 AND d2 = 65 AND
d1 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30) AND
d2 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30)) j
-- Try: [1,45] Fail
-- Try: [10,45] Success!
CROSS APPLY (
SELECT d33=d1, d34=d2, dall, [population]
FROM dbo.NewDistricts
WHERE n = 2 AND d1 = 10 AND d2 = 45 AND
d1 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32) AND
d2 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32)) k
-- Try: [45,46] Fail
-- Try: [12,45] Fail
--CROSS APPLY (
-- SELECT d35=d1, d36=d2, dall, [population]
-- FROM dbo.NewDistricts
-- WHERE n = 2 AND d1 = 12 AND d2 = 45 AND
-- d1 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34) AND
-- d2 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34)) l
--
-- Try: [22,49,50] Success!
CROSS APPLY (
SELECT d35=d1, d36=d2, d37=d3, dall, [population]
FROM dbo.NewDistricts
WHERE n = 3 AND d1 = 22 AND d2 = 49 AND d3 = 50 AND
d1 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34) AND
d2 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34) AND
d3 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34)) l
-- Try: [42,45] Fail
--CROSS APPLY (
-- SELECT d38=d1, d39=d2, dall, [population]
-- FROM dbo.NewDistricts
-- WHERE n = 2 AND d1 = 42 AND d2 = 45 AND
-- d1 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34,d35,d36,d37) AND
-- d2 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34,d35,d36,d37)) m
-- Try: [2,5,47] Fail
-- Try: [7,14,24] Success!
CROSS APPLY (
SELECT d38=d1, d39=d2, d40=d3, dall, [population]
FROM dbo.NewDistricts
WHERE n = 3 AND d1 = 7 AND d2 = 14 AND d3 = 24 AND
d1 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34,d35,d36,d37) AND
d2 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34,d35,d36,d37) AND
d3 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34,d35,d36,d37)) m
-- Try: [2,5,56] Fail
-- Try: [7,14,56] Fail
-- Try: [7,24,61] Fail
-- Try: [15,19,37] Success!
CROSS APPLY (
SELECT d41=d1, d42=d2, d43=d3, dall, [population]
FROM dbo.NewDistricts
WHERE n = 3 AND d1 = 15 AND d2 = 19 AND d3 = 37 AND
d1 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34,d35,d36,d37,d38,d39,d40) AND
d2 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34,d35,d36,d37,d38,d39,d40) AND
d3 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34,d35,d36,d37,d38,d39,d40)) n
-- Try: [2,5,61] Fail
-- Try: [5,14,56] Fail
-- Try: [5,24,61] Fail
-- Try: [7,14,61] Fail
-- Try: [14,24,61] Fail
-- Try: [22,38,49] Fail
-- Try: [5,56,61] Success!
CROSS APPLY (
SELECT d44=d1, d45=d2, d46=d3, dall, [population]
FROM dbo.NewDistricts
WHERE n = 3 AND d1 = 5 AND d2 = 56 AND d3 = 61 AND
d1 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34,d35,d36,d37,d38,d39,d40,d41,d42,d43) AND
d2 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34,d35,d36,d37,d38,d39,d40,d41,d42,d43) AND
d3 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34,d35,d36,d37,d38,d39,d40,d41,d42,d43)) o
-- Try: [14,56,61] Fail
-- Try: [22,38,50] Fail
-- Try: [5,14,61] Fail
-- Try: [15,30,37] Fail
-- Try: [19,30,37] Fail
-- Try: [21,30,41] Success!
CROSS APPLY (
SELECT d47=d1, d48=d2, d49=d3, dall, [population]
FROM dbo.NewDistricts
WHERE n = 3 AND d1 = 21 AND d2 = 30 AND d3 = 41 AND
d1 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34,d35,d36,d37,d38,d39,d40,d41,d42,d43,d44,d45,d46) AND
d2 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34,d35,d36,d37,d38,d39,d40,d41,d42,d43,d44,d45,d46) AND
d3 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34,d35,d36,d37,d38,d39,d40,d41,d42,d43,d44,d45,d46)) p
-- Try: [4,38,50] Fail
-- Try: [4,22,38] Fail
-- Try: [21,33,41] Fail
-- Try: [4,38,54] Success!
CROSS APPLY (
SELECT d50=d1, d51=d2, d52=d3, dall, [population]
FROM dbo.NewDistricts
WHERE n = 3 AND d1 = 4 AND d2 = 38 AND d3 = 54 AND
d1 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34,d35,d36,d37,d38,d39,d40,d41,d42,d43,d44,d45,d46,d47,d48,d49) AND
d2 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34,d35,d36,d37,d38,d39,d40,d41,d42,d43,d44,d45,d46,d47,d48,d49) AND
d3 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34,d35,d36,d37,d38,d39,d40,d41,d42,d43,d44,d45,d46,d47,d48,d49)) q
-- Try: [30,37,41] Fail
-- Try: [30,33,41] Fail
-- Try: [4,16,54] Fail
-- Try: [34,38,50] Fail
-- Try: [15,37,51] Fail
-- Try: [17,21,33] Fail
-- Try: [19,37,51] Fail
-- Try: [22,34,38] Fail
-- Try: [4,34,54] Fail
-- Try: [21,41,51] Fail
-- Try: [16,20,54] Fail
-- Try: [17,18,53] Success!
CROSS APPLY (
SELECT d53=d1, d54=d2, d55=d3, dall, [population]
FROM dbo.NewDistricts
WHERE n = 3 AND d1 = 17 AND d2 = 18 AND d3 = 53 AND
d1 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34,d35,d36,d37,d38,d39,d40,d41,d42,d43,d44,d45,d46,d47,d48,d49,d50,d51,d52) AND
d2 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34,d35,d36,d37,d38,d39,d40,d41,d42,d43,d44,d45,d46,d47,d48,d49,d50,d51,d52) AND
d3 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34,d35,d36,d37,d38,d39,d40,d41,d42,d43,d44,d45,d46,d47,d48,d49,d50,d51,d52)) r
-- Try: [17,33,53] Fail
-- Try: [16,44,54] Fail
-- Try: [21,33,51] Fail
-- Try: [4,16,38] Fail
-- Try: [16,34,54] Fail
-- Try: [17,33,41] Fail
-- Try: [4,34,38] Fail
-- Try: [30,37,51] Fail
-- Try: [30,41,51] Fail
-- Try: [17,18,33] Fail
-- Try: [18,53,60] Fail
-- Try: [37,41,51] Fail
-- Try: [39,53,60] Fail
-- Try: [4,16,20] Fail
-- Try: [18,30,51] Fail
-- Try: [20,29,39] Success!
CROSS APPLY (
SELECT d56=d1, d57=d2, d58=d3, dall, [population]
FROM dbo.NewDistricts
WHERE n = 3 AND d1 = 20 AND d2 = 29 AND d3 = 39 AND
d1 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34,d35,d36,d37,d38,d39,d40,d41,d42,d43,d44,d45,d46,d47,d48,d49,d50,d51,d52,d53,d54,d55) AND
d2 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34,d35,d36,d37,d38,d39,d40,d41,d42,d43,d44,d45,d46,d47,d48,d49,d50,d51,d52,d53,d54,d55) AND
d3 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34,d35,d36,d37,d38,d39,d40,d41,d42,d43,d44,d45,d46,d47,d48,d49,d50,d51,d52,d53,d54,d55)) s
-- Try: [30,33,51] Fail
-- Try: [4,16,44] Fail
-- Try: [16,20,39] Fail
-- Try: [18,37,51] Fail
-- Try: [18,41,51] Fail
-- Try: [20,39,44] Fail
-- Try: [29,39,44] Fail
-- Try: [33,37,51] Fail
-- Try: [33,41,51] Fail
-- Try: [4,29,34] Fail
-- Try: [16,39,44] Fail
-- Try: [20,53,60] Fail
-- Try: [29,34,38] Fail
-- Try: [29,53,60] Fail
-- Try: [4,16,34] Fail
-- Try: [4,34,44] Fail
-- Try: [16,34,38] Fail
-- Try: [17,51,53] Fail
-- Try: [18,33,51] Fail
-- Try: [29,34,39] Fail
-- Try: [34,38,44] Fail
-- Try: [17,53,60] Fail
-- Try: [18,39,60] Fail
-- Try: [20,29,44] Fail
-- Try: [34,39,44] Fail
-- Try: [16,20,44] Fail
-- Try: [16,29,44] Fail
-- Try: [17,30,51] Fail
-- Try: [17,37,51] Fail
-- Try: [17,41,51] Fail
-- Try: [16,20,34] Fail
-- Try: [16,29,34] Fail
-- Try: [18,20,60] Fail
-- Try: [18,29,60] Fail
-- Try: [20,34,44] Fail
-- Try: [20,39,60] Fail
-- Try: [29,34,44] Fail
-- Try: [29,39,60] Fail
-- Try: [16,34,44] Success!
CROSS APPLY (
SELECT d59=d1, d60=d2, d61=d3, dall, [population]
FROM dbo.NewDistricts
WHERE n = 3 AND d1 = 16 AND d2 = 34 AND d3 = 44 AND
d1 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34,d35,d36,d37,d38,d39,d40,d41,d42,d43,d44,d45,d46,d47,d48,d49,d50,d51,d52,d53,d54,d55,d56,d57,d58) AND
d2 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34,d35,d36,d37,d38,d39,d40,d41,d42,d43,d44,d45,d46,d47,d48,d49,d50,d51,d52,d53,d54,d55,d56,d57,d58) AND
d3 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34,d35,d36,d37,d38,d39,d40,d41,d42,d43,d44,d45,d46,d47,d48,d49,d50,d51,d52,d53,d54,d55,d56,d57,d58)) t
-- Try: [17,18,51] Fail
-- Try: [17,33,51] Fail
-- Try: [39,44,60] Fail
-- Try: [17,18,60] Fail
-- Try: [17,33,60] Fail
-- Try: [17,39,60] Fail
-- Try: [20,29,60] Fail
-- Try: [16,20,60] Fail
-- Try: [20,44,60] Fail
-- Try: [29,44,60] Fail
-- Try: [17,20,60] Fail
-- Try: [17,29,60] Fail
-- Try: [18,51,60] Fail
-- Try: [29,34,60] Fail
-- Try: [17,51,60] Fail
--CROSS APPLY (
-- SELECT d62=d1, d63=d2, d64=d3, dall, [population]
-- FROM dbo.NewDistricts
-- WHERE n = 3 AND d1 = 17 AND d2 = 51 AND d3 = 60 AND
-- d1 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34,d35,d36,d37,d38,d39,d40,d41,d42,d43,d44,d45,d46,d47,d48,d49,d50,d51,d52,d53,d54,d55,d56,d57,d58,d59,d60,d61) AND
-- d2 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34,d35,d36,d37,d38,d39,d40,d41,d42,d43,d44,d45,d46,d47,d48,d49,d50,d51,d52,d53,d54,d55,d56,d57,d58,d59,d60,d61) AND
-- d3 NOT IN (base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,d31,d32,d33,d34,d35,d36,d37,d38,d39,d40,d41,d42,d43,d44,d45,d46,d47,d48,d49,d50,d51,d52,d53,d54,d55,d56,d57,d58,d59,d60,d61)) u
)
SELECT n=n+5, np, nt
,[population]=a.[population] + b.[population]
,dall = a.dall + ';[' + CAST(d62 AS VARCHAR) + '];[' + CAST(d63 AS VARCHAR) + '];[' +
CAST(d64 AS VARCHAR) + + '];[' + CAST(d65 AS VARCHAR) + '];[' + CAST(d66 AS VARCHAR) + ']'
FROM CombineNewDistricts a
-- Now add in the districts missed above (there are 5 singlets)
CROSS APPLY (
SELECT d62=SUM([1]), d63=SUM([2]), d64=SUM([3]), d65=SUM([4]), d66=SUM([5])
,[population]=SUM(pt.[population])
FROM (
SELECT District, ElimDist, a.[population]
,n=ROW_NUMBER() OVER (PARTITION BY ElimDist ORDER BY District)
FROM Sweden a
CROSS APPLY (
SELECT CASE
WHEN district IN (d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16, d17
,d18, d19, d20, d21, d22, d23, d24, d25, d26, d27, d28, d29, d30, d31, d32, d33, d34
,d35, d36, d37, d38, d39, d40, d41, d42, d43, d44, d45, d46, d47, d48, d49, d50, d51, d52
,d53, d54, d55, d56, d57, d58, d59, d60, d61)
THEN 0 ELSE 1 END) b(ElimDist)
WHERE ElimDist = 1
) d
PIVOT (MAX(district)
FOR n IN ([1], [2], [3], [4], [5])) pt
) b
SET STATISTICS TIME OFF
Not sure how long this would take to run the first time, but with a little training the optimizer makes short work of it and it runs in the blink of an eye generating the final result I posted above.
Now that is the mother of all cascaded CROSS APPLYs! I suspect, however that you'll call my "try/fail/success" approach sort of a cheat because it doesn't capture the additional time doing a try that fails. So be it.
As I noted, since the manual process I used is repetitous, I believe it can be automated with some diabolically complicated dynamic SQL. Then, we can modify the PF column (and add some additional columns) to provide the ordering for the "try NewDistrict" processing, iterating through the order columns, thus whacking out multiple solutions each time it is run.
I'm going to work on that over the weekend.
I love this challenge!
My thought question: Have you ever been told that your query runs too fast?
My advice:
INDEXing a poor-performing query is like putting sugar on cat food. Yeah, it probably tastes better but are you sure you want to eat it?
The path of least resistance can be a slippery slope. Take care that fixing your fixes of fixes doesn't snowball and end up costing you more than fixing the root cause would have in the first place.
Need to UNPIVOT? Why not CROSS APPLY VALUES instead?[/url]
Since random numbers are too important to be left to chance, let's generate some![/url]
Learn to understand recursive CTEs by example.[/url]
[url url=http://www.sqlservercentral.com/articles/St
July 5, 2012 at 11:15 am
I haven't walked through the code yet, but...
[33,51,60] - is a triple
[55, 66] - is a double
Triples - I only count 9 in your list. Add the above (33,51,60) and it'll be 10 (correct).
Doubles - I think you just miscounted. I count 11 (the last double is repeated in your list), add the [55,66] and it's 12 (correct).
>>Love this challenge!
Yah, I'm a sucker for these from time to time. They're always a great learning experience, if they don't put me over the edge :w00t:
Dr. Les Cardwell, DCS-DSS
Enterprise Data Architect
Central Lincoln PUD
July 5, 2012 at 6:27 pm
Les Cardwell (7/5/2012)
I haven't walked through the code yet, but...[33,51,60] - is a triple
[55, 66] - is a double
Triples - I only count 9 in your list. Add the above (33,51,60) and it'll be 10 (correct).
Doubles - I think you just miscounted. I count 11 (the last double is repeated in your list), add the [55,66] and it's 12 (correct).
>>Love this challenge!
Yah, I'm a sucker for these from time to time. They're always a great learning experience, if they don't put me over the edge :w00t:
There was a minor error in my POC "mother of cascading CROSS APPLY" query that caused the doubling of [10,45]. Note that k.dall was included twice in the top SELECT of CombineNewDistricts. Corrected results:
[2,47,58];[3,52];[6,13];[9,26];[35,63];[48];
[8,25];[36,40];[11,64];[27,62];[28,59];[43,57];[23,32];[31,46];[1,12];[42,65];[10,45];
[22,49,50];[7,14,24];[15,19,37];[5,56,61];[21,30,41];[4,38,54];[17,18,53];[20,29,39];[16,34,44];
[33];[51];[55];[60];[66]
As to the doublet and triplet you identified missing from my results set, obviously great minds think alike! That is the next thing I was going to look for.
Since I spent some time on this last night, I'm on the cusp of what I hope will be the final solution.
Once again, stay tuned!
My thought question: Have you ever been told that your query runs too fast?
My advice:
INDEXing a poor-performing query is like putting sugar on cat food. Yeah, it probably tastes better but are you sure you want to eat it?
The path of least resistance can be a slippery slope. Take care that fixing your fixes of fixes doesn't snowball and end up costing you more than fixing the root cause would have in the first place.
Need to UNPIVOT? Why not CROSS APPLY VALUES instead?[/url]
Since random numbers are too important to be left to chance, let's generate some![/url]
Learn to understand recursive CTEs by example.[/url]
[url url=http://www.sqlservercentral.com/articles/St
July 5, 2012 at 10:26 pm
Les Cardwell (7/5/2012)
[33,51,60] - is a triple[55, 66] - is a double
I checked my NewDistricts table and these two aren't in it but they should be, so I need to go back and check my logic there to see why they weren't included.
The good news, is that I finished writing a Stored Procedure that is able to iterate through multiple, likely solution cases in the time it takes for a hummingbird to flap its wing!
I'll post the whole shootin' match after I figure out what's wrong with my NewDistricts generator posted earlier.
My thought question: Have you ever been told that your query runs too fast?
My advice:
INDEXing a poor-performing query is like putting sugar on cat food. Yeah, it probably tastes better but are you sure you want to eat it?
The path of least resistance can be a slippery slope. Take care that fixing your fixes of fixes doesn't snowball and end up costing you more than fixing the root cause would have in the first place.
Need to UNPIVOT? Why not CROSS APPLY VALUES instead?[/url]
Since random numbers are too important to be left to chance, let's generate some![/url]
Learn to understand recursive CTEs by example.[/url]
[url url=http://www.sqlservercentral.com/articles/St
July 6, 2012 at 12:28 am
dwain.c (7/5/2012)
Les Cardwell (7/5/2012)
[33,51,60] - is a triple[55, 66] - is a double
I checked my NewDistricts table and these two aren't in it but they should be, so I need to go back and check my logic there to see why they weren't included.
The good news, is that I finished writing a Stored Procedure that is able to iterate through multiple, likely solution cases in the time it takes for a hummingbird to flap its wing!
I'll post the whole shootin' match after I figure out what's wrong with my NewDistricts generator posted earlier.
OK Les, now I think you're yanking my chain!
In order for [55,66] to be a valid double, there should be a Map entry for 55/60 (district/neighbor) but that doesn't exist in the data set you provided. In that general vicinity (SW2) I see these only:
INSERT INTO Map VALUES (52,53);
INSERT INTO Map VALUES (53,60);
INSERT INTO Map VALUES (55,61);
INSERT INTO Map VALUES (58,66);
INSERT INTO Map VALUES (63,65);
Also, for [33,51,60] to be a valid triple, I have 33/51 in the Map but I don't have either 33/60 or 51/60 to make the triple.
Here is what I have in that vicinity:
<snip>
INSERT INTO Map VALUES (32,65);
INSERT INTO Map VALUES (33,41);
INSERT INTO Map VALUES (33,51);
INSERT INTO Map VALUES (33,52);
INSERT INTO Map VALUES (34,38);
<snip>
INSERT INTO Map VALUES (48,64);
INSERT INTO Map VALUES (52,53);
Also, going back to Dr. Dobb's original map:
55 doesn't look close to 66, unless you would allow 25/66 but that's not in the map either, i.e., I'm not sure if 55/66 should be in the Map table.
Similar arguments apply the the triplet you said is there. I'm going to go back and review how you're creating those districts but something seems amiss here.
My thought question: Have you ever been told that your query runs too fast?
My advice:
INDEXing a poor-performing query is like putting sugar on cat food. Yeah, it probably tastes better but are you sure you want to eat it?
The path of least resistance can be a slippery slope. Take care that fixing your fixes of fixes doesn't snowball and end up costing you more than fixing the root cause would have in the first place.
Need to UNPIVOT? Why not CROSS APPLY VALUES instead?[/url]
Since random numbers are too important to be left to chance, let's generate some![/url]
Learn to understand recursive CTEs by example.[/url]
[url url=http://www.sqlservercentral.com/articles/St
July 6, 2012 at 12:08 pm
You're right. It's not in my set either...No clue, other than my mind is also on PTO perhaps :ermm:
AAR, just use the select code from SW6 to get valid sets of doubles or triples...
Doubles...
SELECT DISTINCT S1.district, S1.population
FROM Sweden AS S1, Sweden AS S2
WHERE (S1.district < S2.district
OR S1.district = 66 ) --to pick up the last district in doubles (!?)
AND EXISTS (
SELECT *
FROM Remaining AS R1
WHERER1.district3 = 0
ANDS1.district IN ( R1.district1, R1.district2 ) )
ORDER BY S1.district
--------
Triples (note I modified this from SW6 to use Remaining vs. Candidates to eliminate the outlier)...
SELECT DISTINCT S1.district, S1.population
FROM Sweden AS S1, Sweden AS S2
WHERES1.district < S2.district
AND EXISTS (
SELECT *
FROM Remaining AS R1
WHERER1.district3 > 0
ANDS1.district IN (R1.district1, R1.district2, R1.district3 ) )
ORDER BY S1.district
Dr. Les Cardwell, DCS-DSS
Enterprise Data Architect
Central Lincoln PUD
July 8, 2012 at 2:06 pm
Dwain,
Using your CTE, I've come up with a hybrid solution that shortens my initial code considerably. I'll test over the next day or so and post the findings. I also haven't done much with Pivot in the past, but think it might streamline it further. More experiments...
~Les
Dr. Les Cardwell, DCS-DSS
Enterprise Data Architect
Central Lincoln PUD
July 8, 2012 at 6:38 pm
Les Cardwell (7/6/2012)
You're right. It's not in my set either...No clue, other than my mind is also on PTO perhaps :ermm:
Whew! That's a relief. Thought I'd missed an important assumption (again).
Didn't get much time to work on this over the weekend but I'm still planning to plug away. I did get some ideas...
My thought question: Have you ever been told that your query runs too fast?
My advice:
INDEXing a poor-performing query is like putting sugar on cat food. Yeah, it probably tastes better but are you sure you want to eat it?
The path of least resistance can be a slippery slope. Take care that fixing your fixes of fixes doesn't snowball and end up costing you more than fixing the root cause would have in the first place.
Need to UNPIVOT? Why not CROSS APPLY VALUES instead?[/url]
Since random numbers are too important to be left to chance, let's generate some![/url]
Learn to understand recursive CTEs by example.[/url]
[url url=http://www.sqlservercentral.com/articles/St
July 8, 2012 at 9:08 pm
OK. It's time for a checkpoint, if for no other reason that to get straight in my head what I've been doing.
First, I needed to convince myself of something that you probably already knew. That is, if a district participates in a doublet, it does not also participate in a triplet (and vice versa of course). That is confirmed by the following (which returns no rows):
SELECT d
FROM (SELECT d FROM NewDistricts CROSS APPLY (VALUES (d1), (d2)) a(d) WHERE n=2) a
INTERSECT
SELECT d
FROM (SELECT d FROM NewDistricts CROSS APPLY (VALUES (d1), (d2),(d3)) a(d) WHERE n=3) b
I mentioned to you last week that I had an idea on how to generate multiple solutions. In order to do this, we must make a change to the NewDistricts table. I've already given you the DDL to create that table including the PF (packing factor) column. Now we'll add a few columns that I'll explain in a minute.
ALTER TABLE dbo.NewDistricts ADD n1 INT
ALTER TABLE dbo.NewDistricts ADD n2 INT
ALTER TABLE dbo.NewDistricts ADD n3 INT
ALTER TABLE dbo.NewDistricts ADD n4 INT
ALTER TABLE dbo.NewDistricts ADD n5 INT
ALTER TABLE dbo.NewDistricts ADD n6 INT
ALTER TABLE dbo.NewDistricts ADD n7 INT
ALTER TABLE dbo.NewDistricts ADD n8 INT
ALTER TABLE dbo.NewDistricts ADD n9 INT
ALTER TABLE dbo.NewDistricts ADD n10 INT
My initial "mother of all cascading CROSS APPLY queries" was a useful exercise as it established the pattern for what is about to transpire. Recall the "TRY/FAIL/SUCCESS" approach? The first time through I did it manually completely based on the PF column that I have in my NewDistricts TABLE. The new n1, n2, ..., n10 columns we just added to NewDistricts will establish that order for performing the tries based on a packing factor, either the initial one I chose to set up or some other.
I am convinced that the PF, which establishes the order that doublets and triplets are packed into the solution is the key to eventually arriving at the optimal solution. I believe that solution to consist of 16 doublets and 11 triplets. I have achieved the former but the best I've been able to do so far on the triplets is 10. Below I show one of these.
Let's try populating the n columns in our NewDistricts table with the following:
-- Set the initial PF
;WITH CountByOccurrence AS (
SELECT d, [Count], Doublets, Triplets
FROM (
SELECT d, [Count]=SUM([Count]), Doublets=SUM(Doublets), Triplets=SUM(Triplets)
FROM dbo.NewDistricts
CROSS APPLY (VALUES (d1, 1, CASE WHEN n=2 THEN 1 ELSE 0 END, CASE WHEN n=3 THEN 1 ELSE 0 END)
,(d2, 1, CASE WHEN n=2 THEN 1 ELSE 0 END, CASE WHEN n=3 THEN 1 ELSE 0 END)
,(d3, 1, CASE WHEN n=2 THEN 1 ELSE 0 END, CASE WHEN n=3 THEN 1 ELSE 0 END)
) x(d, [Count], Doublets, Triplets)
WHERE d <> 0
GROUP BY d) x),
SpecialDistricts (d1, d2, d3) AS (
SELECT 48, 0, 0 UNION ALL SELECT 35, 63, 0 UNION ALL SELECT 9, 26, 0
UNION ALL SELECT 6, 13, 0 UNION ALL SELECT 3, 52, 0 UNION ALL SELECT 2, 47, 58)
UPDATE nd
SET PF=CASE WHEN sd.d1 IS NULL THEN a+b+c ELSE 0 END
FROM dbo.NewDistricts nd
LEFT OUTER JOIN SpecialDistricts sd ON sd.d1 = nd.d1 AND sd.d2 = nd.d2 AND sd.d3 = nd.d3
INNER JOIN (
SELECT n, d1, d2, d3, a=a.[Count], b=ISNULL(b.[Count], 0), c=ISNULL(c.[Count], 0)
FROM dbo.NewDistricts
LEFT OUTER JOIN CountByOccurrence a ON a.d=d1
LEFT OUTER JOIN CountByOccurrence b ON b.d=d2
LEFT OUTER JOIN CountByOccurrence c ON c.d=d3
) a ON nd.d1 = a.d1 AND nd.d2 = a.d2 AND nd.d3 = a.d3
-- Populate the sort columns for unused rows (i.e., where PF=0)
UPDATE dbo.NewDistricts
SET n1=0, n2=0, n3=0, n4=0, n5=0, n6=0, n7=0, n8=0, n9=0, n10=0
WHERE PF=0
-- Let's try to populate the packing order in our n1, n2, ..., n10 columns
-- Hold off on n2 because we'll populate that one by learning from prior solution
UPDATE a
SET n1=rn1, n3=rn3, n4=rn4, n5=rn5, n6=rn6, n7=rn7, n8=rn8, n9=rn9, n10=rn10
FROM dbo.NewDistricts a
INNER JOIN (
SELECT d1, d2, d3
-- Our baseline (as prior solution): Sort by Packing Factor
,rn1=ROW_NUMBER() OVER (ORDER BY PF, d1, d2, d3)
-- Some completely random sorts (in case we get lucky)
,rn3=ROW_NUMBER() OVER (ORDER BY NEWID())
,rn4=ROW_NUMBER() OVER (ORDER BY NEWID())
,rn5=ROW_NUMBER() OVER (ORDER BY NEWID())
,rn6=ROW_NUMBER() OVER (ORDER BY NEWID())
,rn7=ROW_NUMBER() OVER (ORDER BY NEWID())
,rn8=ROW_NUMBER() OVER (ORDER BY NEWID())
,rn9=ROW_NUMBER() OVER (ORDER BY NEWID())
,rn10=ROW_NUMBER() OVER (ORDER BY NEWID())
FROM dbo.NewDistricts
WHERE PF<>0) b
ON a.d1 = b.d1 AND a.d2 = b.d2 AND a.d3 = b.d3
;WITH Keepers (d1, d2, d3) AS (
SELECT 8, 66, 0 UNION ALL SELECT 25, 55, 0
UNION ALL SELECT 30, 37, 41 UNION ALL SELECT 17, 18, 52)
UPDATE a
SET PF=1
FROM dbo.NewDistricts a
INNER JOIN Keepers b ON a.d1 = b.d1 AND a.d2 = b.d2 AND a.d3 = b.d3
UPDATE a
SET n2=rn2
FROM dbo.NewDistricts a
INNER JOIN (
SELECT d1, d2, d3
-- Optimal solution for doublets but only nearly optimal for triplets
,rn2=ROW_NUMBER() OVER (ORDER BY n, PF, d1, d2, d3)
FROM dbo.NewDistricts
WHERE PF<>0) b
ON a.d1 = b.d1 AND a.d2 = b.d2 AND a.d3 = b.d3
We'll also need a Solutions table and a SP that generates the solution:
-- Create a table to store our solutions
CREATE TABLE dbo.Solutions
(SolutionID INT IDENTITY PRIMARY KEY, n INT, np INT, nt INT
,[Population] INT, [Solution] VARCHAR(320), SQL4Solution NVARCHAR(MAX))
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:Dwain Camps
-- Create date: 05-Jul-2012
-- Description:Dynamic method to resolve the Sweden re-districting problem.
-- =============================================
CREATE PROCEDURE [dbo].[GenerateSwedenRedistrictingSolutions]
@NoSolutions INT = 1
AS
BEGIN
SET NOCOUNT ON;
-- Declare some variables to use in building the Dynamic SQL
DECLARE @InitialCTE NVARCHAR(MAX)
,@CountOfn NVARCHAR(MAX) = 'n=12'
,@CountOfnp NVARCHAR(MAX) = 'np=4'
,@CountOfnt NVARCHAR(MAX) = 'nt=1'
,@Pop NVARCHAR(MAX) = 'population=base.[population]'
,@dall NVARCHAR(MAX) = 'dall=base.dall'
,@BaseDistricts NVARCHAR(MAX) = 'd1,d2,d3,d4,d5,d6,d7,d8,d9,d10,d11,d12'
,@CADoublets NVARCHAR(MAX)
,@CATriplets NVARCHAR(MAX)
,@DistrictColumns NVARCHAR(MAX) = 'base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12'
,@SQL4Districts NVARCHAR(MAX) = ''
,@SQL2Keep NVARCHAR(MAX) = ''
,@SQL2Try NVARCHAR(MAX) = ''
,@SQL2Save NVARCHAR(MAX) = ''
,@WorkingCAs NVARCHAR(MAX) = ''
-- Declare some loop and counter variables
DECLARE @DerivedTabCounter INT = 1
,@SolutionCounter INT = 1
,@CurrentDistrict INT = 13
,@CurrentTryCounter INT = 1
-- Holders for districts read from NewDistricts
,@d1 INT
,@d2 INT
,@d3 INT
,@TotalNewDistricts INT
,@rc INT
,@Lastnp INT
,@Lastnt INT
,@Lastn INT
-- Junk (mostly): to dump the Try resuls so they don't get displayed
,@n INT
,@np INT
,@nt INT
,@da VARCHAR(320)
,@population INT
-- Loop for each potential solution in columns n1, n2, ... of NewDistricts
WHILE @SolutionCounter <= @NoSolutions
BEGIN
SELECT
@InitialCTE =
';WITH BaseDistricts AS (' +
' SELECT d1=[1], d2=[2], d3=[3], d4=[4], d5=[5], d6=[6] ' +
' ,d7=[7], d8=[8], d9=[9], d10=[10], d11=[11], d12=[12] ' +
' ,dall, [population] ' +
' FROM ( ' +
' SELECT d, n1=ROW_NUMBER() OVER (ORDER BY d) ' +
' FROM dbo.NewDistricts nd ' +
' CROSS APPLY (VALUES (d1), (d2), (d3)) b(d) ' +
' WHERE n' + CAST(@SolutionCounter AS VARCHAR) + '= 0 AND d<>0)a ' +
' PIVOT (MAX(d) FOR n1 IN ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12])) pt ' +
' CROSS APPLY ( ' +
' SELECT dall=STUFF(( ' +
' SELECT '';'' + dall FROM dbo.NewDistricts WHERE PF = 0 ' +
' FOR XML PATH(''''), TYPE).value(''.'', ''VARCHAR(MAX)''), 1, 1, '''') ' +
' ,[population]=SUM([population]) ' +
' FROM dbo.NewDistricts ' +
' WHERE n' + CAST(@SolutionCounter AS VARCHAR) + ' = 0) a) '
SELECT @TotalNewDistricts = COUNT(*)-6 FROM NewDistricts
PRINT 'Starting calculation of solution number: ' + CAST(@SolutionCounter AS VARCHAR) + ' ' +
CONVERT(VARCHAR(100),GETDATE(),121)
WHILE @CurrentTryCounter <= @TotalNewDistricts
BEGIN
-- SELECT the next NewDistrict to try
SELECT @SQL4Districts = 'SELECT @d1=d1, @d2=d2, @d3=d3 FROM NewDistricts WHERE n' +
CAST(@SolutionCounter AS VARCHAR) + '=@ctc'
EXEC sp_executesql @SQL4Districts
,@params=N'@ctc INT, @d1 INT OUTPUT, @d2 INT OUTPUT, @d3 INT OUTPUT'
,@ctc=@CurrentTryCounter, @d1=@d1 OUTPUT, @d2=@d2 OUTPUT, @d3=@d3 OUTPUT
SELECT -- Create the SQL for the cascading, correlated CROSS APPLYs
@CADoublets =
' CROSS APPLY (' +
'SELECT d' + CAST(@CurrentDistrict AS VARCHAR) + '=d1, d' +
CAST(@CurrentDistrict+1 AS VARCHAR) + '=d2, dall, [population] ' +
'FROM dbo.NewDistricts ' +
'WHERE n = 2 AND d1 = ' + CAST(@d1 AS VARCHAR) + ' AND d2 = ' +
CAST(@d2 AS VARCHAR) + ' AND ' +
' d1 NOT IN (' + @DistrictColumns + ') AND' +
' d2 NOT IN (' + @DistrictColumns + ')) a' + CAST(@DerivedTabCounter AS VARCHAR)
,@CATriplets =
' CROSS APPLY (' +
'SELECT d' + CAST(@CurrentDistrict AS VARCHAR) + '=d1, d' +
CAST(@CurrentDistrict+1 AS VARCHAR) + '=d2, d' +
CAST(@CurrentDistrict+2 AS VARCHAR) + '=d3, dall, [population] ' +
'FROM dbo.NewDistricts ' +
'WHERE n = 3 AND d1 = ' + CAST(@d1 AS VARCHAR) + ' AND d2 = ' +
CAST(@d2 AS VARCHAR) + ' AND d3 = ' + CAST(@d3 AS VARCHAR) + ' AND' +
' d1 NOT IN (' + @DistrictColumns + ') AND ' +
' d2 NOT IN (' + @DistrictColumns + ') AND ' +
' d3 NOT IN (' + @DistrictColumns + ')) a' + CAST(@DerivedTabCounter AS VARCHAR)
-- Build the base SQL to try
SELECT @SQL2Try = @InitialCTE + ' SELECT @' + @CountOfn + ',@' +
@CountOfnp + ',@' + @CountOfnt + ',@' +
@pop + ',@' + @dall + ' FROM BaseDistricts base ' + @WorkingCAs +
CASE WHEN @d3 = 0 THEN @CADoublets ELSE @CATriplets END
--IF @SolutionCounter > 1
--PRINT 'Trying ' + CASE WHEN @d3 = 0 THEN 'doublet' ELSE 'triplet' END + ': [' +
-- CAST(@d1 AS VARCHAR) + ',' + CAST(@d2 AS VARCHAR) +
-- CASE WHEN @d3 = 0 THEN ']' ELSE ',' + CAST(@d3 AS VARCHAR) + ']' END
EXEC sp_executesql @SQL2Try
,@params=N'@n INT OUTPUT, @np INT OUTPUT, @nt INT OUTPUT, @dall VARCHAR(320) OUTPUT, @population INT OUTPUT'
,@n=@n OUTPUT, @np=@np OUTPUT, @nt=@nt OUTPUT, @dall=@da OUTPUT, @population=@population OUTPUT
SELECT @rc=@@ROWCOUNT
--IF @SolutionCounter > 1
--PRINT 'Result: ' + CASE @rc WHEN 0 THEN 'Fail!' ELSE 'Success!' END + ' for try count: ' +
-- CAST(@CurrentTryCounter AS VARCHAR)
-- If row count above found a row, then we've got a good NewDistrict to add to our list
IF @rc <> 0 SELECT
@Lastn = @n + CASE WHEN @d3 = 0 THEN 2 ELSE 3 END
,@Lastnp = @np + CASE WHEN @d3 = 0 THEN 1 ELSE 0 END
,@Lastnt = @nt + CASE WHEN @d3 = 0 THEN 0 ELSE 1 END
,@WorkingCAs = @WorkingCAs + CASE WHEN @d3 = 0 THEN @CADoublets ELSE @CATriplets END
,@CountOfn = @CountOfn + CASE WHEN @d3 = 0 THEN '+2' ELSE '+3' END
,@CountOfnp = @CountOfnp + CASE WHEN @d3 = 0 THEN '+1' ELSE '+0' END
,@CountOfnt = @CountOfnt + CASE WHEN @d3 <> 0 THEN '+1' ELSE '+0' END
,@DistrictColumns = @DistrictColumns + ',d' + CAST(@CurrentDistrict AS VARCHAR) +
',d' + CAST(@CurrentDistrict+1 AS VARCHAR) +
CASE WHEN @d3 = 0 THEN '' ELSE ',d' + CAST(@CurrentDistrict+2 AS VARCHAR) END
,@BaseDistricts = @BaseDistricts + ',d' + CAST(@CurrentDistrict AS VARCHAR) +
',d' + CAST(@CurrentDistrict+1 AS VARCHAR) +
CASE WHEN @d3 = 0 THEN '' ELSE ',d' + CAST(@CurrentDistrict+2 AS VARCHAR) END
,@CurrentDistrict = @CurrentDistrict + CASE WHEN @d3 = 0 THEN 2 ELSE 3 END
,@Pop = @pop + '+a' + CAST(@DerivedTabCounter AS VARCHAR) + '.[Population]'
,@dall = @dall + '+'';''+a' + CAST(@DerivedTabCounter AS VARCHAR) + '.dall'
,@DerivedTabCounter = @DerivedTabCounter + 1
-- Let's give it another try shall we?
SELECT @CurrentTryCounter = @CurrentTryCounter + 1
END -- Of WHILE (try CROSS APPLYs)
-- Build the SQL to execute to save the solution
IF @Lastnp * 2 + @Lastnt * 3 = 65 -- No need for the fancy stuff (optimal solution)
BEGIN
-- First the SQL to save in the Solutions table
SELECT @SQL2Save = @InitialCTE + ' SELECT ' + @CountOfn + ',' +
@CountOfnp + ',' + @CountOfnt + ',' + @pop + ',' + @dall + ',' + @BaseDistricts +
' FROM BaseDistricts base ' + @WorkingCAs
-- Our SQL to execute is just the final query we constructed above (with an INSERT)
SELECT @SQL2Keep = @InitialCTE + ' INSERT INTO dbo.Solutions ' +
' SELECT ' + @CountOfn + ',' +
@CountOfnp + ',' + @CountOfnt + ',' + @pop + ',' + @dall + ',' + @BaseDistricts +
',SQL4Solution=N''' + REPLACE(@SQL2Save, '''', '''''') + '''' +
' FROM BaseDistricts base ' + @WorkingCAs
END
ELSE BEGIN
-- If not optimal solution, it's necessary to add a PIVOT to include the eliminated districts
-- First the SQL to save in the Solutions table
;WITH Tally (m) AS (
SELECT TOP (66-@Lastn) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM sys.all_columns),
SpecialVars AS (
SELECT m, Col4Pivot='[' + CAST(m AS VARCHAR) + ']'
,MissingDistrict='d' + CAST(@CurrentDistrict + m - 1 AS VARCHAR)
FROM Tally)
SELECT @SQL2Save = @InitialCTE + ', CombineNewDistricts AS ( SELECT ' +
@CountOfn + ',' +
@CountOfnp + ',' + @CountOfnt + ',' + @pop + ',' + @dall + ',' + @BaseDistricts +
' FROM BaseDistricts base ' + @WorkingCAs + ') ' +
' SELECT n=n+' + CAST(66-@Lastn AS VARCHAR) +
',np,nt,[population]=a.[population] + b.[population],dall = a.dall + '';' +
STUFF((
SELECT '''];['' + CAST(' + MissingDistrict + ' AS VARCHAR) +'
FROM SpecialVars
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'), 1, 3, '') + ''']''' +
',' + @BaseDistricts +
( SELECT ',' + MissingDistrict
FROM SpecialVars
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)') +
' FROM CombineNewDistricts a CROSS APPLY ( SELECT ' +
STUFF((
SELECT ',' + MissingDistrict + '=SUM(' + Col4Pivot + ')'
FROM SpecialVars
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'), 1, 1, '') + ', ' +
'[population]=SUM(pt.[population]) FROM (SELECT District, ElimDist, a.[population]' +
',n=ROW_NUMBER() OVER (PARTITION BY ElimDist ORDER BY District) FROM Sweden a ' +
'CROSS APPLY (SELECT CASE WHEN district IN (' + @BaseDistricts + ') ' +
'THEN 0 ELSE 1 END) b(ElimDist) WHERE ElimDist = 1) d PIVOT (MAX(district) FOR n IN (' +
STUFF((
SELECT ',' + Col4Pivot
FROM SpecialVars
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'), 1, 1, '') +
')) pt) b'
-- Next is nearly the same as above except it INSERTs into Solutions TABLE
;WITH Tally (m) AS (
SELECT TOP (66-@Lastn) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM sys.all_columns),
SpecialVars AS (
SELECT m, Col4Pivot='[' + CAST(m AS VARCHAR) + ']'
,MissingDistrict='d' + CAST(@CurrentDistrict + m - 1 AS VARCHAR)
FROM Tally)
SELECT @SQL2Keep = @InitialCTE + ', CombineNewDistricts AS ( SELECT ' +
@CountOfn + ',' +
@CountOfnp + ',' + @CountOfnt + ',' + @pop + ',' + @dall + ',' + @BaseDistricts +
' FROM BaseDistricts base ' + @WorkingCAs + ') ' + 'INSERT INTO dbo.Solutions ' +
' SELECT n=n+' + CAST(66-@Lastn AS VARCHAR) +
',np,nt,[population]=a.[population] + b.[population],dall = a.dall + '';' +
STUFF((
SELECT '''];['' + CAST(' + MissingDistrict + ' AS VARCHAR) +'
FROM SpecialVars
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'), 1, 3, '') + ''']''' +
',SQL4Solution=N''' + REPLACE(@SQL2Save, '''', '''''') + '''' +
' FROM CombineNewDistricts a CROSS APPLY ( SELECT ' +
STUFF((
SELECT ',' + MissingDistrict + '=SUM(' + Col4Pivot + ')'
FROM SpecialVars
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'), 1, 1, '') + ', ' +
'[population]=SUM(pt.[population]) FROM (SELECT District, ElimDist, a.[population]' +
',n=ROW_NUMBER() OVER (PARTITION BY ElimDist ORDER BY District) FROM Sweden a ' +
'CROSS APPLY (SELECT CASE WHEN district IN (' + @BaseDistricts + ') ' +
'THEN 0 ELSE 1 END) b(ElimDist) WHERE ElimDist = 1) d PIVOT (MAX(district) FOR n IN (' +
STUFF((
SELECT ',' + Col4Pivot
FROM SpecialVars
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'), 1, 1, '') +
')) pt) b'
END
EXEC sp_executesql @SQL2Keep
PRINT 'Completed calculation of solution number: ' + CAST(@SolutionCounter AS VARCHAR) + ' ' +
CONVERT(VARCHAR(100),GETDATE(),121)
-- Increment the solution counter and re-initialize all working variables
SELECT @SolutionCounter = @SolutionCounter + 1
,@CountOfn = 'n=12'
,@CountOfnp = 'np=4'
,@CountOfnt = 'nt=1'
,@Pop = 'population=base.[population]'
,@dall = 'dall=base.dall'
,@BaseDistricts = 'd1,d2,d3,d4,d5,d6,d7,d8,d9,d10,d11,d12'
,@DistrictColumns = 'base.d1,base.d2,base.d3,d4,d5,d6,d7,d8,d9,d10,d11,d12'
,@CurrentTryCounter = 1
,@CurrentDistrict = 13
,@Lastn = NULL
,@Lastnp = NULL
,@Lastnt = NULL
,@WorkingCAs = ''
END -- Of WHILE (solution iterations)
END
GO
I know... That's a pretty convoluted SP. It does seem to work to my intention though.
You can now run the 10 solutions we set up as follows:
DECLARE @StartTime DATETIME = GETDATE()
EXEC GenerateSwedenRedistrictingSolutions 10 -- Run solutions 1 through 10
SELECT StartTime=@StartTime, EndTime=GETDATE(), TotalMS=DATEDIFF(millisecond, @StartTime, GETDATE())
SELECT * FROM dbo.Solutions
ORDER BY 2*np+3*nt DESC, SolutionID
This last script will take about 12.5 minutes to run through, but yours may take a little longer because SQL server was already trained on the first 2 solutions (once trained they only take a couple of seconds to run). Looking at the results in the Solutions table, we see that:
SolutionID=1: Matches the solution we provided earlier.
SolutionID=2: Now has the optimal doublets (16) and 10 triplets.
The other (random) solutions weren't nearly as good. Here is what SolutionID 2 looks like (you can run just the SQL stored in the Solutions column SQL4Solution to get just that one solution):
[2,47,58];[3,52];[6,13];[9,26];[35,63];[48];
[8,66];[25,55];[36,40];[11,64];[27,62];[28,59];[43,57];[23,32];[31,46];[1,12];[42,65];[10,45];
[30,37,41];[22,49,50];[7,14,24];[5,56,61];[4,38,54];[17,21,33];[18,53,60];[20,29,39];[16,34,44];
[15];[19];[51]
My idea at this point, is to try moving various triplets earlier in the packing order, by looking at the districts that were ineligible in SolutionID=2 (and then successive solution attempts).
Of course, a smarter person than me (but I may give it a try anyway) may be able to analyze the triplets in more depth to come up with an alternative packing order based on some other factor(s).
Another approach, may be to set up enough numbered n columns for all possible triplet sort combinations. And then let the SP rip through them all. At least it seems to process each additional solution in a linear time increment.
My thought question: Have you ever been told that your query runs too fast?
My advice:
INDEXing a poor-performing query is like putting sugar on cat food. Yeah, it probably tastes better but are you sure you want to eat it?
The path of least resistance can be a slippery slope. Take care that fixing your fixes of fixes doesn't snowball and end up costing you more than fixing the root cause would have in the first place.
Need to UNPIVOT? Why not CROSS APPLY VALUES instead?[/url]
Since random numbers are too important to be left to chance, let's generate some![/url]
Learn to understand recursive CTEs by example.[/url]
[url url=http://www.sqlservercentral.com/articles/St
July 9, 2012 at 8:23 pm
Eureka! I have just hit upon a method that generates "hundreds" of triplet combinations that can be combined 11 at a time to cover all the districts included in the triplets! And it does it in a reasonable run time.
I say "hundreds" because I'm not sure they're all unique, they may be permutations of the same combinations. So I need to work that out. But it is just window dressing.
Edit: There are actually 8 unique triplet combinations. I also need to work out if this approach will work on doublets but I think it will.
I've said it before, stay tuned! I'll post more code when I've got it all sorted out.
My thought question: Have you ever been told that your query runs too fast?
My advice:
INDEXing a poor-performing query is like putting sugar on cat food. Yeah, it probably tastes better but are you sure you want to eat it?
The path of least resistance can be a slippery slope. Take care that fixing your fixes of fixes doesn't snowball and end up costing you more than fixing the root cause would have in the first place.
Need to UNPIVOT? Why not CROSS APPLY VALUES instead?[/url]
Since random numbers are too important to be left to chance, let's generate some![/url]
Learn to understand recursive CTEs by example.[/url]
[url url=http://www.sqlservercentral.com/articles/St
July 10, 2012 at 9:43 am
I haven't had a chance to come up for air to review the latest, but only 8 unique triplets? I get 162...
I did try to use the CTE in place of SW7/SW8, but after processing for an hour on the SW8 equivalent, I killed it. I must be missing something.
~Les
Dr. Les Cardwell, DCS-DSS
Enterprise Data Architect
Central Lincoln PUD
July 11, 2012 at 1:52 am
Les Cardwell (7/10/2012)
I haven't had a chance to come up for air to review the latest, but only 8 unique triplets? I get 162...I did try to use the CTE in place of SW7/SW8, but after processing for an hour on the SW8 equivalent, I killed it. I must be missing something.
~Les
Patience kind sir! As any good programmer would on a long running solution, I had a governor in place to stop upon finding the first set of optimal triples!
Indeed, when I removed that governor, I found 162 triplet combinations that achieve the optimal solution.
Likewise, there are 58 optimal doublet solutions!
Combining these, we have 9396 (162*58) optimal re-districting solutions, for which I have compiled the final query. Putting it together in steps now for your perusal.
Soon, kind sir, soon! You will find that I think the whole thing will run in under 20 minutes.
My thought question: Have you ever been told that your query runs too fast?
My advice:
INDEXing a poor-performing query is like putting sugar on cat food. Yeah, it probably tastes better but are you sure you want to eat it?
The path of least resistance can be a slippery slope. Take care that fixing your fixes of fixes doesn't snowball and end up costing you more than fixing the root cause would have in the first place.
Need to UNPIVOT? Why not CROSS APPLY VALUES instead?[/url]
Since random numbers are too important to be left to chance, let's generate some![/url]
Learn to understand recursive CTEs by example.[/url]
[url url=http://www.sqlservercentral.com/articles/St
July 11, 2012 at 6:21 am
How does generating all possible optimal solutions in under 6 minutes grab ya?
Attached is a write up on the solution including everything you need to run it yourself. I am interested in how the timing compares on your machine vs. your old solution.
For any interested parties that have been following with mirth my trials, tribulations and machinations on the road to a solution (god help me!) any solution to this challenge, but have corporate rules against downloading ZIP attachments, PM me your email address and I'd be happy to send it along.
I can't tell you how much fun this has been! Best forum post I've ever tackled.
My thought question: Have you ever been told that your query runs too fast?
My advice:
INDEXing a poor-performing query is like putting sugar on cat food. Yeah, it probably tastes better but are you sure you want to eat it?
The path of least resistance can be a slippery slope. Take care that fixing your fixes of fixes doesn't snowball and end up costing you more than fixing the root cause would have in the first place.
Need to UNPIVOT? Why not CROSS APPLY VALUES instead?[/url]
Since random numbers are too important to be left to chance, let's generate some![/url]
Learn to understand recursive CTEs by example.[/url]
[url url=http://www.sqlservercentral.com/articles/St
July 11, 2012 at 9:32 am
Ha! I'm glad I could send you on such an adventure 🙂
I'll run both for time comparisons this weekend when there's not much activity on this server (this has been one of those server nightmares where the vendor's specs we're not even close to what it took to bring things under control).
On the plus side, I learned a great deal about CTE's.
Left wanting is the elusive MK solution, but JC's bin packing methodology opened a door. More fodder for meditation in waiting rooms.
Dr. Les Cardwell, DCS-DSS
Enterprise Data Architect
Central Lincoln PUD
Viewing 15 posts - 91 through 105 (of 109 total)
You must be logged in to reply to this topic. Login to reply