2

I have a table tblEntrants which is a list of pool players. I want to draw fixtures for them so I need to take the top half of the list and play them against the bottom half of the list by copying them into tblFixtures.

tblFixtures has 3 columns: player1, player2 and compID.

This is what I have so far:

INSERT INTO tblFixtures (player1, compID)
    SELECT TOP (50) PERCENT 
        accountID, compID 
    FROM 
        tblEntrants
    WHERE 
        paid = 'y'  AND compID = @compID 
    ORDER BY 
        accountID ASC

INSERT INTO tblFixtures (player2)
    SELECT TOP (50) PERCENT accountID 
    FROM tblEntrants
    WHERE paid = 'y' AND compID = @compID 
    ORDER BY accountID DESC

But this does this...

Player1   Player 2   CompID
---------------------------
Bob     v null       {Guid}
Bill    v null       {Guid}
Ben     v null       {Guid}
null    v Matt       {Guid}
null    v Mick       {Guid}
null    v Mark       {Guid}

I need to adjust the above code to add the bottom half of the players in to the Player2 column where the first half of the players exist. Like this..

Player1   Player 2   CompID
---------------------------
Bob     v Matt       {Guid}
Bill    v Mick       {Guid}
Ben     v Mark       {Guid}

Any help appreciated

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
  • Your question makes no sense to me. Why can't you just insert all three columns at once? – Tim Biegeleisen Oct 20 '17 at 01:12
  • Edit your question and provide sample data and desired results. – Gordon Linoff Oct 20 '17 at 01:13
  • Because the number of entrants will vary.. so if theres 32 players in tblEntrants, I need the top 16 to play the bottom 16.. I'm fairly new to programming so unsure how to do that. – Eddie Bonce Oct 20 '17 at 01:17
  • There's no guarantee the first set of players will be different to the second set of players. In short your second statement needs to be an update instead of an insert, and you need to add some logic so it doesn't repick the same players again.... all of which I can assist with if you want. – Nick.Mc Oct 20 '17 at 01:27
  • @Nick.McDermaid Thanks that would be great. Ideally I would like it produce the fixtures randomly but I thought If I ensured the list was always even then the top half vs bottom half would never have the same player appear twice. – Eddie Bonce Oct 20 '17 at 01:39
  • Sorry I just saw the order by so yes, they should be mutually exclusive after all – Nick.Mc Oct 20 '17 at 01:40
  • Yeah I just need to do an update somehow but unsure how to do that as it needs to update for each row which has a player1 – Eddie Bonce Oct 20 '17 at 01:42
  • The usual way of picking random rows is using `ORDER BY NEWID()`. Let me have a think about the `UPDATE` statement and I'll post an answer – Nick.Mc Oct 20 '17 at 01:45
  • @Nick.McDermaid Really appreciated cheers. – Eddie Bonce Oct 20 '17 at 01:46

3 Answers3

1

First insert rows randomly

INSERT INTO tblFixtures (player1, compID)
SELECT TOP (50) PERCENT accountID, compID FROM tblEntrants
WHERE paid = 'y'  
AND compID=@compID 
ORDER BY NEWID()

Without a primary key in the tblFixtures table, this is all I can think of to update the table. It's a loop (there's almost always a way to avoid a loop in SQL!!). Also you could probably write some mega SQL statement that does it in one go... but hopefully this makes sense.

DECLARE @player1 uniqueidentifier , @player2 uniqueidentifier ;

SELECT 1;

WHILE @@ROWCOUNT <> 0
BEGIN

    SELECT @player1= NULL, @player2=NULL;

    PRINT 'Finding unmatched player';

    -- Find a player without an opponent
    SELECT TOP 1 @player1 = player1
    FROM tblFixtures
    WHERE compID=@compID 
    AND player2 IS NULL;

    PRINT 'Found unmatched player ' + CAST(@player1 AS VARCHAR(100));

    -- Find a player not in a fixture
    SELECT TOP 1 @player2 = accountID 
    FROM tblEntrants
    WHERE paid = 'y'  
    AND compID=@compID 
    AND accountID  NOT IN (
         SELECT player1 
         FROM tblFixtures 
         WHERE compID=@compID
         AND player1 IS NOT NULL
         )
    AND accountID  NOT IN (
         SELECT player2 
         FROM tblFixtures 
         WHERE compID=@compID
         AND player2 IS NOT NULL
         )


    PRINT 'Found unassigned player ' + CAST(@player2 AS VARCHAR(100));

    -- Assign the opponent
    UPDATE tblFixtures
    SET player2 = @player2
    WHERE player1 = @player1
    AND compID = @compID;

END
Nick.Mc
  • 18,304
  • 6
  • 61
  • 91
  • Wow thanks. I understand some of that. It's giving the error Invalid column name 'accountID' so just trying to figure that out. – Eddie Bonce Oct 20 '17 at 02:07
  • Double click the error and it'll show which line is the issue. I just took some guesses about your table columns so that's what this issue is – Nick.Mc Oct 20 '17 at 02:07
  • Something about INT as well... Should it be uniqueidentifier and not INT ? – Eddie Bonce Oct 20 '17 at 02:13
  • tblentrants uses accountID and tblfixtures uses player1, player2 and compID – Eddie Bonce Oct 20 '17 at 02:14
  • No idea - you'll need to post your full table definitions so that I know what datatypes your various fields are. Can you edit your original question and post table definitions - right click on your table in object explorer and press "script table as create" and post that. – Nick.Mc Oct 20 '17 at 02:14
  • I'm using MS SQL online with 1&1 (Web thingy). I've managed to get it to execute but when I look at tblfixtures.. player2 is all nulls – Eddie Bonce Oct 20 '17 at 02:22
  • At this point I don't know what your tables look like and I don't know what script you ran to get it to work... but I will add some logging that will give us some clues – Nick.Mc Oct 20 '17 at 02:24
  • I just changed accountID to player1 like in your edit, INT to uniqueidentifier in the DECLARE's and changed AND compID=@compID to..... WHERE compID=@compID in both places – Eddie Bonce Oct 20 '17 at 02:29
  • OK I'll fix up the code so we're looking at the same thing – Nick.Mc Oct 20 '17 at 02:30
  • I fixed the code and made some more changes. I guess either the entrant or opponent select is failing to select something. The print messages (which will appear in the Messages area) will hopefully give you a clue – Nick.Mc Oct 20 '17 at 02:35
  • It's almost working.. just the last 2 players under player2 are the same.. eg: 1st row: Bob v Matt 2nd row: Bill v Mick 3rd row: Ben v Mick So Mick appears twice. Oooh so close. Thank you again for helping. – Eddie Bonce Oct 20 '17 at 02:44
  • That's pretty hard to solve. Not too sure why that would be. Do the log messages give any clues – Nick.Mc Oct 20 '17 at 02:49
  • [Win32Exception (0x80004005): The wait operation timed out] [SqlException (0x80131904): Execution Timeout Expired. The timeout period elapsed prior to completion of the operation or the server is not responding. Finding unmatched player Found unmatched player 8E977698-C5F2-4469-B97B-46732EFD7B06 Found unassigned player 3E828BB1-AEC2-4543-BD26-E12AD886D2E8 Finding unmatched player Found unmatched player 98F34FA8-F28E-4E44-9E13-F1BCB4755BF5 Found unassigned player 4F9C3964-2672-4413-8A01-2ED09CA7A711 Finding unmatched player THE LAST 3 LINES ABOVE REPEAT HUNDREDS OF TIMES – Eddie Bonce Oct 20 '17 at 02:55
  • Thats the stack trace. I can see the table in SQL and its just those last 2 players which are the same. But when I run it on the site itself I get this error/stacktrace. – Eddie Bonce Oct 20 '17 at 02:57
  • Ooops there's an infinite loop there cause there's some kind of edge case thing happening. – Nick.Mc Oct 20 '17 at 02:59
  • OOOOPS my fault.. I had a player set as not paid :-) All working, thank you so much. You're a superstar! – Eddie Bonce Oct 20 '17 at 03:02
  • It's still timing out on the site but the table sets up correctly – Eddie Bonce Oct 20 '17 at 03:04
  • Yeah something infinite is happening :-) I've unmarked this as answered incase it didn't let us chat. I will mark it though. Do you know what's causing the infinity ? – Eddie Bonce Oct 20 '17 at 03:09
  • It's possible that an odd number of players might cause the infinite loop. I'd have to set up a test case to see. This would be easier if your posted your table definitions – Nick.Mc Oct 20 '17 at 04:17
  • There's definitely an even amount of players. Not sure how to post table definitions. Is it not just the WHILE @@ROWCOUNT <> 0 ? Doesn't this say just keep going if there's more than 0 rows? – Eddie Bonce Oct 20 '17 at 08:31
  • Alternatively, is there a way to check that no nulls appear in player2 column and if so then it exits the loop? – Eddie Bonce Oct 20 '17 at 09:22
  • `WHILE @@ROWCOUNT <> 0` mean while the _last statement_ returned more than one row, continue. So if the last update actually updated anything, continue. So the last statement updated something. Anyway I already explained in an earlier comment: _right click on your table in object explorer and press "script table as create" and post that.right click on your table in object explorer and press "script table as create" and post that._ - i.e. in SQL Server Management Studio. – Nick.Mc Oct 20 '17 at 12:28
1

Consider a cross join of top and bottom half players, so every player is paired with every possible match with another player and add a WHERE clause in case of an uneven list and median cut-off is the same player. And in SQL Server ORDER BY is allowed in a derived table with TOP specified.

INSERT into tblFixtures (player1, player2, compID)
SELECT p1.accountID, p2.accountID, p1.compID
FROM
    (SELECT TOP (50) PERCENT accountID, compID FROM tblEntrants
     WHERE paid = 'y' AND compID=@compID ORDER BY accountID ASC) p1
CROSS JOIN 
    (SELECT TOP (50) PERCENT accountID FROM tblEntrants
     WHERE paid = 'y' AND compID=@compID ORDER BY accountID DESC) p2
WHERE p1.accountID <> p2.accountID

Do note: CROSS JOIN can become expensive very quickly as it returns a cartesian product (M x N) where total rows is the product of both sets.

Parfait
  • 104,375
  • 17
  • 94
  • 125
  • This works but puts all outcomes of each player playing each other so with 6 players in tblentrants.. It generates 9 fixtures where everyone plays each other. Just needs to be top 3 playing bottom 3 in this example. – Eddie Bonce Oct 20 '17 at 03:18
  • Really? The 9 returns should be each person in top 3 playing each of the bottom 3: 3 X 3 = 9. No one should be facing someone in its own half. Is this a one-on-one pool game? Did you intend for only 3 matches here? – Parfait Oct 20 '17 at 17:24
1
INSERT into tblFixtures (player1, compID, player2)
SELECt top1.accountID,compID, least1.accountID
  FROM
    (SELECT accountID, compID , 
            ROW_NUMBER() OVER (ORDER BY accountID) rn
       FROM tblEntrants
      WHERE paid = 'y'  AND compID=@compID) top1,

    (SELECT accountID ,
            ROW_NUMBER() OVER (ORDER BY accountID desc) rn
       FROM tblEntrants
    WHERE paid = 'y'  AND compID=@compID ) least1
WHERE top1.rn = least1.rn
  AND top1.rn <= (select round(count(*)/2,0) from tblEntrants where paid = 'y' and compID=@compID )
Valli
  • 1,440
  • 1
  • 8
  • 13
  • This doesnt seem to like the '.' – Eddie Bonce Oct 20 '17 at 03:21
  • No it's the FullStops it doesn't like. top.rn and top.accountID throwing an error – Eddie Bonce Oct 20 '17 at 09:18
  • May be top is a reserved word and I can't use it as alias. Edited – Valli Oct 20 '17 at 09:31
  • Now it gives me the very vague Error: Incorrect syntax near the keyword 'END'. – Eddie Bonce Oct 20 '17 at 09:37
  • Wow.. very close.. It generates the table but with 6 players in tblentrants it generates 6 matches instead of 3. It looks like it repeats but swaps the players round so it does this... 1st row: Bob v Matt 2nd row: Bill v Mick 3rd row: Ben v Mark Which is where it should stop but also does.. 4th row: Mark v Ben 5th row: Mick v Bill 6th row Matt v Bob – Eddie Bonce Oct 20 '17 at 09:53
  • It should give only 3 rows.. Am trying to restrict the rows here..top1.rn <= (select round(count(*)/2,0) from tblEntrants).. can you try hardcoding top1.rn <= 3 – Valli Oct 20 '17 at 10:18
  • Yeah that works correctly when I do that. Any ideas? – Eddie Bonce Oct 20 '17 at 10:23
  • I got why its giving more than 3.. i missed to do the checks on paid and compID while retrieving the count.. edited.. hope it works – Valli Oct 20 '17 at 10:23
  • 1
    That's it :-) Awesome, thank you so much. Very well explained. – Eddie Bonce Oct 20 '17 at 10:28