Buffalo will not host a game in Toronto this year. The point of an annual game was to turn Buffalo into a regional team, pulling in millions of potential fans in and around Toronto. Unfortunately, it turns out that hosting one crappy game a year hasn’t done much for the team, so they’re postponing this idea. I think this makes it all the more likely that Buffalo moves after Ralph Wilson passes on. That would be a shame for a long-running AFC franchise, but Buffalo’s a dead town in a dead region with a team whose last winning record was in the prior millennium.
March 11, 2014
Beyond the Box Score invented a stat today, called SCRAP (for Self Contained Replacement Average Player). It includes the ability to score runs (albeit, in a very insignificant way), being hit by pitches, getting caught stealing, bunting, and grounding into double plays. The objective is to get as close to zero as possible. David Eckstein scores…
a 1.29. Of all the players surveyed, in fact, he was least scrappy. The scrappiest career, with a SCRAP of .32, was Pete Kozma. Sorry, David Eckstein. I bet it was your sophomore year, when you ranked 11th in the MVP debate (no, seriously). Oddly enough, that was also the only year you had an OPS+ greater than 100.
Maybe you should have sucked more. (I’m surprised Juan Pierre and Willie Bloomquist weren’t included, actually.)
March 10, 2014
The Obama administration, in the course of defending ARRA, try to argue that they aren’t falling for Frederic Bastiat’s broken window fallacy. The problem is, they don’t understand the parable. They’re essentially saying to people, “Hey, we know what you really want, and trust us: it’s better than what you would have wasted your money on.” They wrap their spending preferences in fancy terms and try to pretend that it will be better than what people would have spent their money on had they the opportunity.
That is the fallacy here, and exactly what the Obama administration has done with ARRA.
March 9, 2014
It appears that U.S. intelligence had no idea that Russia’s military was going to invade Ukraine. If only they had spied on the other 2/3 of Americans’ calls, they certainly would have gotten this one!
March 8, 2014
GOG, one of the finest gaming distribution platforms on Earth, had a sale a few months back, in which the eager individual could acquire an old favorite and one I had wanted to go back to but didn’t. (Alpha Centauri — plus the expansion! — and System Shock 2.) Total cost: roughly $3.
I haven’t gotten back into Alpha Centauri yet, although I think it’s just a matter of time. I’ve mainly playing System Shock 2 and… well…
A bit of background. Anyone who has followed this site for any length of time knows that one of my all time favorite PC games is Deus Ex. (Dragon Age: Origins, the Mass Effect Trilogy, and Skyrim would all be somewhere near the top as well, in terms of sheer number of hours played, as well as EUIII/EUIV and Vicky 2). Deus Ex was introduced to me by a very good friend shortly after it was released; the same friend recommended System Shock 2, loaning it to me so I could play it. 2000 was an excellent year for gaming, though. I played System Shock 2 for a couple of weeks, but never for very long, and eventually turned elsewhere.
As I grew older, I found myself stumbling upon more and more games in the System Shock 2 lineage. Thief was another game in this lineage — roughly contemporary with System Shock 2, released in 1998 — that I played and set aside, although it was the stupid zombies that kept me away. (I later returned to Thief and regretted not giving it another chance.)
Other games of the System Shock heritage include Bioshock and Dead Space (although the latter is made by different people.)
It is now 2014. I’ve spent a few hours the couple of weeks on System Shock 2 and…
I still don’t like it very much. It’s not the graphics — that didn’t stop me from devouring Thief and Thief 2, not to mention Deus Ex. I like the RPG element, the horror is relatively well done and sparse. It’s just not sucking me in. I think I know why, too.
Let’s consider the games of the System Shock 2 dynasty. Direct successors include Thief (2/3 — haven’t played the new one yet), Deus Ex (Invisible War/Human Revolution), and Bioshock (2/Infinite). The first Thief aside (which was all kinds of weird), they all deep, fulfilling plots. System Shock 2 just doesn’t resonate in the same way. Maybe if I played the original System Shock, I’d care more about the plot. I doubt it, though. The problem is you’re never really introduced to the people you’re supposed to be caring about. You just get the audio logs. Audio logs, done right, are very effective at story telling (witness Bioshock: Infinite). These are just insignificant details of insignificant people. SHODAN is properly impressive, but without a reason to care about the humans on the Von Braun, you kind of want her to win.
I do think I’ll finish it. It’ll just take some time.
March 7, 2014
Will Leitch, funny man, says a lot of what I’ve been saying about PEDs for a long time.
March 4, 2014
Over on the right-hand bar, I have a page listing all of the T-SQL anti-patterns in this series. There are additional anti-patterns, code smells, and things to avoid, but I think 14 is enough for one presentation. Speaking of presentations, check it out at SQL Saturday #277 in Richmond, Virginia on Saturday, March 22nd, 2014.
Publisher: Fantasy Flight Games
Type of game: Card game, bluffing, Cold War awesomeness
Number of players: 2
Learning curve: Easy+
Estimated time to play (first time/subsequent times): 45 minutes/30 minutes
Estimated setup time: 4 minutes
I found this game for sale for about $20, and it’s a tremendous value. Each player chooses a side, KGB or CIA, (which are functionally identical) and competes for influence in various countries around the globe while killing each other’s spies.
If that sounds awesome, it’s because it is. It is the second greatest Cold War game of all time. (Third when East vs. West comes out.) At the beginning of each turn, you and your opponent each choose one of six spies, and place them face down. Depending on who wins the country phase, you could kill the opposing spy, snatch victory from the jaws of defeat, or even better, absolutely nothing. During the country phase, you each compete either for a special card (worth 5 influence) or a country (5 to 20 — might be some as high as 30). First one to 200 influence wins.
Each country has a certain level of stability; if you go past it, you trigger a civil war and lots of bad things happen. (Avoiding it is as simple as grade school level arithmetic, coupled with probability.) Crudely put, it’s a bit like blackjack, but you can “kill” the other guy’s cards, or take them for yourself.
Calling it loads of fun is an understatement. The only caveat is that the rules take two or three readings to get a grip on; they’re written a bit poorly (the actual rules are quite easy to understand). Other than that, I have nothing but praise for this one. Highly recommended!
March 3, 2014
My final T-SQL anti-pattern that I’m going to discuss is using GUIDs as a clustered index. This is a particularly pernicious problem because it’s so easy for developers to fall into it.
A GUID, or globally unique identifier, is a 16-byte field which is (supposed to be) truly unique. You can run into duplicates when dealing with large enough data sets on enough servers, but let’s not worry about that here. Instead, I’m going to focus on the performance implications of this. Let’s say that we have a web application which hits three tables: Contract, Contact, and ContractContact. We have people (Contacts), we have agreements (Contracts), and we have a listing of which people are associated with which agreements (ContractContacts). Our web developer came up with a great page in which you can enter the contract details and tie a person to a contract on the same page at the same time. That web developer knows about identity integer columns, but because he wants all of the logic to happen in a C# business layer but take place in a single T-SQL transaction, he can’t get the identity integer value. As such, he grabs the Guid.NewGuid() method in .NET and passes in a GUID as a unique identifier for Contract. Because this GUID is unique, our developer creates a primary key on the GUID. He tests the logic out and it works fine in development.
Then he moves it out to prod and failure occurs. Paul Randal and Kim Tripp pointed out that this could have been a plausible explanation for why the Windows 7 RC download process was so problematic. The reason is that, if you use a GUID as a primary key, by default, SQL Server also makes that the clustered key. And having a GUID as a clustered key is a terrible idea. Given how unique and random GUIDs are, they’re a perfect way of maximizing page splits and I/O operations whenever you insert new rows. This introduces additional, unnecessary table fragmentation and hurts performance of SELECT queries.
So what alternatives do we have here? Let’s list a few:
- Use identity integer columns. in our Contract example, the web developer didn’t want to do that, mostly because he didn’t want to create a stored procedure with Contract inputs followed by ContractContact_ContactID1, ContractContact_ContactID2, etc. Avoiding this is totally reasonable, and if you’re using SQL Server 2008 or later, writing a stored procedure like this is totally unnecessary. Instead, use a table-valued parameter to store your list of contact IDs. The great news is that you can build them in .NET quite easily. Then, in your creation stored procedure, you can get the contract ID’s identity value and insert ContractContact records within the same transaction, so if there’s a problem during insertion, you don’t have to worry about backing part of the data out.
- Use a sequence column instead of an identity integer value. Starting in SQL Server 2012, we can use sequences rather than identity integers. The main advantage here is that, by following a standard method, we can get guaranteed unique values before inserting rows into the Contract table. That way, we can assign ContractID on each ContractContact object and still perform all of our .NET operations in a single T-SQL transaction.
- Use a non-clustered GUID as your primary key. This is a last alternative, when the other two just won’t work. In this case, you can create a GUID in the business layer and use it for web operations, but have an identity column as your clustered key. There are a couple of problems with this solution as opposed to #1 and #2. First of all, you have an additional 16 bytes per row, so with a large enough table, that adds up: 1 billion rows means 16 GB of additional storage. In addition to that base storage addition, you’re also responsible for a non-clustered index on the GUID, so there’s another 16 GB of data. Finally, whenever you look up an object by its GUID value, you’re going to perform a bookmark lookup, meaning that the number of reads to grab a row will double. This will make SELECT operations slower.
Getting off of GUIDs can be an arduous process, but the advantage is that your database will now scale a lot better.
March 2, 2014
Yesterday’s anti-pattern, non-SARGable ORs in joins, was all about writing code which the SQL Server database engine can optimize. Today’s anti-pattern is in that vein, but this time we’re looking at cross-server queries.
Linked servers are a powerful feature within SQL Server, and I don’t want to throw them out entirely. What I do want to do, however, is limit how often they are used in regular, business-critical code. The reason is that, prior to SQL Server 2012 SP1, table statistics were not available to linked server users without sysadmin, db_owner, or db_ddladmin rights. The number of cases in which I would want a linked server role with sysadmin, db_owner, or db_ddladmin rights is very small, and I would definitely not want to give those rights to a web application or anything else user-facing. Without those table statistics, SQL Server would make poor choices with linked server queries, and so joins against remote tables would get very expensive.
Even with those statistics, cross-server joins are going to be expensive. The reason is that in order to join row sets from two servers, you need to pass all of one server’s row set over to the other server, perform the join operation, and then (potentially) pass the full result set back to the first server. If you’re joining two tables with a billion rows, that could be extremely expensive, even if you have great indexes on both sides.
So how do we get around this? Here are a few potential methods:
- Get rid of the linked servers altogether and use another method, such as replication or Always-On Availability Groups, to propagate the data you need from your remote server. This makes a lot of sense if you’re looking at relatively static lookup data.
- For truly static data, you might keep non-replicated copies on both servers. Replication can be a pain to troubleshoot and manage and AGs require 2012 Enterprise Edition so they can be very expensive. Thus, if the data never changes or only changes once a year, it might make more sense to create the yearly change script and execute it on multiple servers.
- If you absolutely need the remote data, try to re-write queries to avoid joining local tables to remote tables. Create a temporary table on your local server, pull in as few rows from the remote server as possible, and then join your local tables to the temp table you created. You still take the linked server hit, but there’s a much smaller chance of that query blowing up.