February 2, 2015 at 6:27 pm
sqldriver (2/2/2015)
Ah, yeah, it's not much more rows than this. I guess the point of the secondary relationships is 'route choosing' of sorts. Like, if you start with BarX, these are your next possible choices (scotch in common) and then these are your next choices (secondary scotch in common) and then these are your next choices (tertiary scotch in common) and so on, until either all possible unique choices (bars) are exhausted, or all are exhausted and un-matched choices (bars) are displayed. If you picture that as a single list column that allows someone to choose a 'search path', it may make more sense.Thanks
Normally one would expect route selection to be determined by bar proximity rather than stock choices.
Unless of course you have a truck that can carry only two or three scotch brands at a time.
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
February 2, 2015 at 6:34 pm
Proximity on a small scale, but on a larger scale you'd need to choose connecting paths. Or even on a not very large scale; think about a subway system. Certain routes go to certain stops with certain connecting lines. That's probably the more apt analogy.
The point of showing unique, earliest occurring here, would be to show the first station you could switch lines, for example.
Thanks
February 2, 2015 at 11:01 pm
sqldriver (2/2/2015)
Eirikur Eiriksson (2/2/2015)
Quick thought, the problem itself is straightforward, the issue is more the schema, normalize it and the solution is easy.😎
That was one of the first things I did. There were about a dozen other columns in this load set that I had to do stuff with, but are irrelevant to this.
I posted the data up like this because I thought it might make it a bit easier for you to consume, and because it made it a lot easier for me to mask 😉
Anyway, I messed a bit with, after something like below, making a table of bars, and of scotch, and then a bunch of 'satellite' scotch per bar tables, all with unique IDs. I just got nowhere with this one.
SELECT Bar, Scotch, Item as ScotchNorm
INTO #WobblyNorm
FROM #Wobbly AS w
CROSS APPLY master.dbo.DelimitedSplit8K(Scotch, ';') AS dsk
SELECT *
FROM #WobblyNorm AS wn
As I said earlier, normalize the data and this problem becomes elementary, see the code for some hints on normalization.
😎
USE tempdb;
GO
SET NOCOUNT ON;
IF OBJECT_ID(N'tempdb..#Wobbly') IS NOT NULL DROP TABLE #Wobbly;
CREATE TABLE #Wobbly (
Bar VARCHAR(5),
Scotch VARCHAR(50)
)
INSERT #Wobbly
( Bar, Scotch )
SELECT 'Bar1','Laphroaig;Lagavulin;Macallan' UNION ALL
SELECT 'Bar2','Talisker;Bowmore' UNION ALL
SELECT 'Bar3','Ardbeg;Oban' UNION ALL
SELECT 'Bar4','Speyburn;Dewars' UNION ALL
SELECT 'Bar5','Glenmorangie;Dalmore' UNION ALL
SELECT 'Bar6','Dalmore' UNION ALL
SELECT 'Bar7','Glenlivet;Talisker;Balvenie;Glengoyne;Springbank' UNION ALL
SELECT 'Bar8','Glenmorangie;Bruichladdich' UNION ALL
SELECT 'Bar9','Laphroaig;Dewars;HighlandPark;Speyburn' UNION ALL
SELECT 'Bar10','Laphroaig;Ardbeg;DunBheagan;' UNION ALL
SELECT 'Bar11','Dalmore;Talisker' UNION ALL
SELECT 'Bar12','Talisker;Oban;Tomintoul;Kilchomin' UNION ALL
SELECT 'Bar13','Bruichladdich;Glenfiddich' UNION ALL
SELECT 'Bar14','AnCnoc;Bruichladdich;Dalwhinnie;Tomintoul' UNION ALL
SELECT 'Bar15','Dalmore;Teachers' UNION ALL
SELECT 'Bar16','OldPulteney;Arberlour;Macallan' UNION ALL
SELECT 'Bar17','Glenmorangie;CompassBox' UNION ALL
SELECT 'Bar18','Longrow;Glenrothes;Dewars' UNION ALL
SELECT 'Bar19','Tomintoul' UNION ALL
SELECT 'Bar20','Glenmorangie;Bunnahabhain' UNION ALL
SELECT 'Bar21','Dalmore;Tomintoul'
IF OBJECT_ID(N'dbo.TBL_BAR') IS NOT NULL
BEGIN
ALTER TABLE dbo.TBL_BAR_WHISKY DROP CONSTRAINT FK_DBO_TBL_BAR_WHISKY_BAR_ID_DBO_TBL_BAR_BAR_ID;
DROP TABLE dbo.TBL_BAR;
END
CREATE TABLE dbo.TBL_BAR
(
BAR_ID INT IDENTITY(1,1) NOT NULL CONSTRAINT PK_DBO_TBL_BAR_BAR_ID PRIMARY KEY CLUSTERED
,BAR_NAME VARCHAR(50) NOT NULL CONSTRAINT UNQ_DBO_TBL_BAR_BAR_NAME UNIQUE
);
IF OBJECT_ID(N'dbo.TBL_WHISKY') IS NOT NULL
BEGIN
ALTER TABLE dbo.TBL_BAR_WHISKY DROP CONSTRAINT FK_DBO_TBL_BAR_WHISKY_WHISKY_ID_DBO_TBL_WHISKY_WHISKY_ID;
DROP TABLE dbo.TBL_WHISKY;
END
CREATE TABLE dbo.TBL_WHISKY
(
WHISKY_ID INT IDENTITY(1,1) NOT NULL CONSTRAINT PK_DBO_TBL_WHISKY_WHISKY_ID PRIMARY KEY CLUSTERED
,WHISKY_NAME VARCHAR(50) NOT NULL CONSTRAINT UNQ_DBO_TBL_WHISKY_WHISKY_NAME UNIQUE
);
IF OBJECT_ID(N'dbo.TBL_BAR_WHISKY') IS NOT NULL DROP TABLE dbo.TBL_BAR_WHISKY;
CREATE TABLE dbo.TBL_BAR_WHISKY
(
BAR_ID INT NOT NULL
,WHISKY_ID INT NOT NULL
,CONSTRAINT PK_DBO_TBL_BAR_WHISKY_BAR_ID_WHISKY_ID PRIMARY KEY CLUSTERED (BAR_ID,WHISKY_ID)
,CONSTRAINT FK_DBO_TBL_BAR_WHISKY_BAR_ID_DBO_TBL_BAR_BAR_ID FOREIGN KEY (BAR_ID) REFERENCES dbo.TBL_BAR(BAR_ID)
,CONSTRAINT FK_DBO_TBL_BAR_WHISKY_WHISKY_ID_DBO_TBL_WHISKY_WHISKY_ID FOREIGN KEY (WHISKY_ID) REFERENCES dbo.TBL_WHISKY(WHISKY_ID)
);
INSERT INTO dbo.TBL_BAR(BAR_NAME)
SELECT
W.Bar
FROM #Wobbly W;
INSERT INTO dbo.TBL_WHISKY(WHISKY_NAME)
SELECT DISTINCT
X.Item
FROM #Wobbly W
CROSS APPLY dbo.DelimitedSplitL8K(W.Scotch,CHAR(59)) AS X
WHERE LEN(X.Item) > 0;
INSERT INTO dbo.TBL_BAR_WHISKY (BAR_ID,WHISKY_ID)
SELECT
BAR.BAR_ID
,WHISKY.WHISKY_ID
FROM dbo.TBL_BAR BAR
INNER JOIN #Wobbly W
ON BAR.BAR_NAME = W.Bar
CROSS APPLY dbo.DelimitedSplitL8K(W.Scotch,CHAR(59)) AS X
INNER JOIN dbo.TBL_WHISKY WHISKY
ON X.Item = WHISKY.WHISKY_NAME;
SELECT
BAR.BAR_NAME
,WHISKY.WHISKY_NAME
FROM dbo.TBL_BAR BAR
INNER JOIN dbo.TBL_BAR_WHISKY BW
ON BAR.BAR_ID = BW.BAR_ID
INNER JOIN dbo.TBL_WHISKY WHISKY
ON BW.WHISKY_ID = WHISKY.WHISKY_ID
;
SELECT
BW.BAR_ID
,BW.WHISKY_ID
,COUNT(BW.BAR_ID) OVER
(
PARTITION BY BW.WHISKY_ID
) AS CNT_BAR_WHISKY
,COUNT(BW.WHISKY_ID) OVER
(
PARTITION BY BW.BAR_ID
) AS CNT_WHISKY_BAR
FROM dbo.TBL_BAR_WHISKY BW;
Query example
SELECT
BW.BAR_ID
,BW.WHISKY_ID
,BAR.BAR_NAME
,COUNT(BW.WHISKY_ID) OVER
(
PARTITION BY BW.BAR_ID
) AS CNT_WHISKY_BAR
,STUFF((SELECT
CONCAT(CHAR(44),W.WHISKY_NAME)
FROM dbo.TBL_WHISKY W
INNER JOIN dbo.TBL_BAR_WHISKY WB
ON W.WHISKY_ID = WB.WHISKY_ID
WHERE WB.BAR_ID = BW.BAR_ID
FOR XML PATH(''),TYPE).value('.[1]','VARCHAR(500)'),1,1,'') AS WHISKY_LIST
,WHISKY.WHISKY_NAME
,COUNT(BW.BAR_ID) OVER
(
PARTITION BY BW.WHISKY_ID
) AS CNT_BAR_WHISKY
,STUFF((SELECT
CONCAT(CHAR(44),B.BAR_NAME)
FROM dbo.TBL_BAR B
INNER JOIN dbo.TBL_BAR_WHISKY WB
ON B.BAR_ID = WB.BAR_ID
WHERE WB.WHISKY_ID = BW.WHISKY_ID
FOR XML PATH(''),TYPE).value('.[1]','VARCHAR(500)'),1,1,'') AS BAR_LIST
FROM dbo.TBL_BAR BAR
INNER JOIN dbo.TBL_BAR_WHISKY BW
ON BAR.BAR_ID = BW.BAR_ID
INNER JOIN dbo.TBL_WHISKY WHISKY
ON BW.WHISKY_ID = WHISKY.WHISKY_ID;
Results
BAR_ID WHISKY_ID BAR_NAME CNT_WHISKY_BAR WHISKY_LIST WHISKY_NAME CNT_BAR_WHISKY BAR_LIST
----------- ----------- ---------- -------------- -------------------------------------------------------- ------------------ -------------- -----------------------------
14 1 Bar14 4 AnCnoc,Bruichladdich,Dalwhinnie,Tomintoul AnCnoc 1 Bar14
16 2 Bar16 3 Arberlour,Macallan,OldPulteney Arberlour 1 Bar16
10 3 Bar10 3 Ardbeg,DunBheagan,Laphroaig Ardbeg 2 Bar3,Bar10
3 3 Bar3 2 Ardbeg,Oban Ardbeg 2 Bar3,Bar10
7 4 Bar7 5 Balvenie,Glengoyne,Glenlivet,Springbank,Talisker Balvenie 1 Bar7
2 5 Bar2 2 Bowmore,Talisker Bowmore 1 Bar2
8 6 Bar8 2 Bruichladdich,Glenmorangie Bruichladdich 3 Bar8,Bar13,Bar14
13 6 Bar13 2 Bruichladdich,Glenfiddich Bruichladdich 3 Bar8,Bar13,Bar14
14 6 Bar14 4 AnCnoc,Bruichladdich,Dalwhinnie,Tomintoul Bruichladdich 3 Bar8,Bar13,Bar14
20 7 Bar20 2 Bunnahabhain,Glenmorangie Bunnahabhain 1 Bar20
17 8 Bar17 2 CompassBox,Glenmorangie CompassBox 1 Bar17
15 9 Bar15 2 Dalmore,Teachers Dalmore 5 Bar5,Bar6,Bar11,Bar15,Bar21
21 9 Bar21 2 Dalmore,Tomintoul Dalmore 5 Bar5,Bar6,Bar11,Bar15,Bar21
11 9 Bar11 2 Dalmore,Talisker Dalmore 5 Bar5,Bar6,Bar11,Bar15,Bar21
5 9 Bar5 2 Dalmore,Glenmorangie Dalmore 5 Bar5,Bar6,Bar11,Bar15,Bar21
6 9 Bar6 1 Dalmore Dalmore 5 Bar5,Bar6,Bar11,Bar15,Bar21
14 10 Bar14 4 AnCnoc,Bruichladdich,Dalwhinnie,Tomintoul Dalwhinnie 1 Bar14
9 11 Bar9 4 Dewars,HighlandPark,Laphroaig,Speyburn Dewars 3 Bar4,Bar9,Bar18
4 11 Bar4 2 Dewars,Speyburn Dewars 3 Bar4,Bar9,Bar18
18 11 Bar18 3 Dewars,Glenrothes,Longrow Dewars 3 Bar4,Bar9,Bar18
10 12 Bar10 3 Ardbeg,DunBheagan,Laphroaig DunBheagan 1 Bar10
13 13 Bar13 2 Bruichladdich,Glenfiddich Glenfiddich 1 Bar13
7 14 Bar7 5 Balvenie,Glengoyne,Glenlivet,Springbank,Talisker Glengoyne 1 Bar7
7 15 Bar7 5 Balvenie,Glengoyne,Glenlivet,Springbank,Talisker Glenlivet 1 Bar7
8 16 Bar8 2 Bruichladdich,Glenmorangie Glenmorangie 4 Bar5,Bar8,Bar17,Bar20
5 16 Bar5 2 Dalmore,Glenmorangie Glenmorangie 4 Bar5,Bar8,Bar17,Bar20
20 16 Bar20 2 Bunnahabhain,Glenmorangie Glenmorangie 4 Bar5,Bar8,Bar17,Bar20
17 16 Bar17 2 CompassBox,Glenmorangie Glenmorangie 4 Bar5,Bar8,Bar17,Bar20
18 17 Bar18 3 Dewars,Glenrothes,Longrow Glenrothes 1 Bar18
9 18 Bar9 4 Dewars,HighlandPark,Laphroaig,Speyburn HighlandPark 1 Bar9
12 19 Bar12 4 Kilchomin,Oban,Talisker,Tomintoul Kilchomin 1 Bar12
1 20 Bar1 3 Lagavulin,Laphroaig,Macallan Lagavulin 1 Bar1
1 21 Bar1 3 Lagavulin,Laphroaig,Macallan Laphroaig 3 Bar1,Bar9,Bar10
10 21 Bar10 3 Ardbeg,DunBheagan,Laphroaig Laphroaig 3 Bar1,Bar9,Bar10
9 21 Bar9 4 Dewars,HighlandPark,Laphroaig,Speyburn Laphroaig 3 Bar1,Bar9,Bar10
18 22 Bar18 3 Dewars,Glenrothes,Longrow Longrow 1 Bar18
16 23 Bar16 3 Arberlour,Macallan,OldPulteney Macallan 2 Bar1,Bar16
1 23 Bar1 3 Lagavulin,Laphroaig,Macallan Macallan 2 Bar1,Bar16
3 24 Bar3 2 Ardbeg,Oban Oban 2 Bar3,Bar12
12 24 Bar12 4 Kilchomin,Oban,Talisker,Tomintoul Oban 2 Bar3,Bar12
16 25 Bar16 3 Arberlour,Macallan,OldPulteney OldPulteney 1 Bar16
9 26 Bar9 4 Dewars,HighlandPark,Laphroaig,Speyburn Speyburn 2 Bar4,Bar9
4 26 Bar4 2 Dewars,Speyburn Speyburn 2 Bar4,Bar9
7 27 Bar7 5 Balvenie,Glengoyne,Glenlivet,Springbank,Talisker Springbank 1 Bar7
7 28 Bar7 5 Balvenie,Glengoyne,Glenlivet,Springbank,Talisker Talisker 4 Bar2,Bar7,Bar11,Bar12
2 28 Bar2 2 Bowmore,Talisker Talisker 4 Bar2,Bar7,Bar11,Bar12
11 28 Bar11 2 Dalmore,Talisker Talisker 4 Bar2,Bar7,Bar11,Bar12
12 28 Bar12 4 Kilchomin,Oban,Talisker,Tomintoul Talisker 4 Bar2,Bar7,Bar11,Bar12
15 29 Bar15 2 Dalmore,Teachers Teachers 1 Bar15
19 30 Bar19 1 Tomintoul Tomintoul 4 Bar12,Bar14,Bar19,Bar21
21 30 Bar21 2 Dalmore,Tomintoul Tomintoul 4 Bar12,Bar14,Bar19,Bar21
12 30 Bar12 4 Kilchomin,Oban,Talisker,Tomintoul Tomintoul 4 Bar12,Bar14,Bar19,Bar21
14 30 Bar14 4 AnCnoc,Bruichladdich,Dalwhinnie,Tomintoul Tomintoul 4 Bar12,Bar14,Bar19,Bar21
Edit: added query sample
February 3, 2015 at 8:08 am
Hi Eirikur,
I feel a lot better about how I set things up now. That's about the identical schema that I made after the loading files were staged, with the exception of a wider table that contained all the values, and a horrible EAV table of just the codes :pinch:
The thing I couldn't figure out, regardless of the schema and normalization, was how to query to present data the way end users were requesting. What I found awkward was creating a hierarchy where anything could be the top node, and some top nodes would result in multiple parents (scotches), and then traversing it without hitting the same bar more than once. What Nevyn nailed that I didn't, was not making the rCTE first the first CTE in the chain. If you see a better way of getting there, I'm wide open to suggestions (and always impressed with your solutions, but you know that already!).
Thanks
February 3, 2015 at 12:32 pm
Hi Joe,
I did take steps to normalize it. The data as I presented it is how I received it in a load file. Not making excuses, but I didn't want the way I normalized it to restrict anyone's solution. I presented it as I received it because I am able to duplicate whatever schema suits the best solution. If that ends up being 1NF, then good, because I already have it normalized that way.
Also, I did read a couple articles of yours that I thought might be relevant HERE[/url], and citing the pilot example HERE[/url]. There was another article of yours that I recalled, about finding workers with certain skills that match up to job postings, but I couldn't find it off the bat.
While I did find the Relational Division article helpful and informative, I wasn't sure how to best apply it as I need it, in what I've started calling a KOTH hierarchy situation. Like a King of The Hill match, any team can be on top, and the hierarchy has to be built based on that, recursively. Does that clarify?
Thanks
February 4, 2015 at 9:12 am
Cool, thanks Joe. I'll see what I can come up with based on this.
February 4, 2015 at 4:55 pm
If this is Romley's division (I'm sorry but I did not recognize it as such, which of course doesn't mean that it is not), Peter Larsson proposes a faster method in the comments posted near the bottom of this article:
High Performance Relational Division in SQL Server[/url]
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
February 6, 2015 at 9:17 am
I think that is the first time a DML has made me salivate;
I'm off home now to Select * from Bar
:sick:
Viewing 8 posts - 16 through 22 (of 22 total)
You must be logged in to reply to this topic. Login to reply