Welcometo our website, where counting becomes effortless and fun. We sincerely appreciate your visit and trust in our free customizable tally counters to track your events accurately. Whether you're managing attendance at a conference, keeping score during a sports event, or simply indulging in your tally counting needs, our widgets are here to make your task easier.
Tally counting goes beyond mere tracking; it becomes an integral part of your event's rhythm. The steady clicks or digital beeps create a symphony of data, ensuring accurate counts and seamless operations. Our tally counters not only simplify your tasks but also enhance the overall experience for both you and your participants. You can confidently focus on engaging with your audience, knowing that our reliable counting devices have got you covered.
At
tallycounterstore.com, we are proud to foster a vibrant community of event organizers, sports enthusiasts, and passionate tally counters. We love hearing your success stories, tips, and unique counting techniques. Connect with like-minded individuals, share your experiences, and stay updated on the latest trends in tally counting. Together, we can elevate the way events are managed and make counting an art form.
Once again, thank you for choosing our free online tally counter widgets. We are thrilled to be a part of your counting journey. Should you have any questions or require assistance, our dedicated support team is just a click away. Happy counting and may your events be filled with success!
There is updated code in this article: Tally Oh! An Improved SQL 8K CSV Splitter Function. Please use that code for production purposes and not the code in this article. The updated code is located here: The New Splitter Functions.
You'll find the makings of a "CSV Splitter" in this article. Please understand that it's for the explanation of how a Tally Table can be used in place of a WHILE loop and that it is, by no stretch of imagination, an optimal solution for a "CSV Splitter. For an optional solution, please refer to the following URL ( +Table/72993/ ) instead of using the code from this article for a "CSV Splitter" of your own.
I actually started out writing an article on how to pass 1, 2, and 3 dimensional "arrays" as parameters in stored procedures. Suddenly it dawned on me that a lot of people still have no clue what a "numbers" or "Tally" table is, never mind how it actually works.
There are dozens of things we can do in SQL that require some type of iteration. "Iteration" means "counters" and "loops" to most people and recursion to others. To those well familiar in the techniques of "Set-based" programming, it means a "Numbers" or "Tally" table, instead. I like the name "Tally" table because, well, it just sounds cooler and there's no chance of anyone mistaking what I said when I say "Tally Table". Everyone immediately knows what I'm talking about and what it's used for. If they don't, they always stop and ask.
So, with that in mind, we're going to explore what a Tally table is and how it works to replace loops. We'll start out simple and build to the classic example of "splitting" a parameter. We'll throw in the added bonus of how to normalize an entire table worth of a CSVColumn... all with no cursors, no loops, no functions, and no performance problems.
A Tally table is nothing more than a table with a single column of very well indexed sequential numbers starting at 0 or 1 (mine start at 1) and going up to some number. The largest number in the Tally table should not be just some arbitrary choice. It should be based on what you think you'll use it for. I split VARCHAR(8000)'s with mine, so it has to be at least 8000 numbers. Since I occasionally need to generate 30 years of dates, I keep most of my production Tally tables at 11,000 or more which is more than 365.25 days times 30 years.
There are many methods to build a Tally table. Let's use one of the most obvious, a loop. Why? Because it's familiar ground for a lot of folks... trust me for a minute... I have other points to make in this article...
The first time you run it, it'll take about 220 milliseconds so, right up front, it beats the loop. Run it a second time and you'll find that it takes less time... anywhere from 73 to 186 milliseconds depending on what the system is doing.
The key here is that we've already replaced one loop using an IDENTITY and a CROSS JOIN. Cross Joins can be a real friend because they can be used in place of loops and, on a properly indexed table like a Tally table, they run nasty fast. The reason why this method IS my favorite is because of the Cross Join... it makes the code very short, very easy to remember, and very fast. We'll see another example of a Cross Join actually using the Tally table later in the article.
Right after the "Hello World" problem, most programming language instructors teach how to "Loop". The problem normally manifests itself as "Produce and display a count from 1 to 10. In SQL Server, that frequently (and, unfortunately) is demonstrated as the following...
This works because we do iterations using a counter. Let me repeat, we do iterations using a counter. This can be defined as "For each count from 1 to 10, display the value of the count".
Also notice, the SELECT was executed 10 times and that creates 10 different result sets. That's NOT set based programming. That's what I refer to as "RBAR" (pronounced "ree-bar" and is a "Modenism" for "Row By Agonizing Row"). Let's see how to do that with a Tally table...
This works because we use existing rows to do the "iterations". Let me repeat, we do the iterations using existing rows. This can be defined as "For each row from 1 to 10, display the value of the row". The BIG difference is that we only use a single SELECT and we don't actually have to count as we go. We just limit how many rows we use and what the values of the rows are. It produces the same result as the loop does (count of 1 to 10), but it does it using a single SELECT. In other words, it produces a single result set with the entire answer. Nothing RBAR about that... that's set based programming.
Note that as the loop progresses, the character counter "steps" through the string using SUBSTRING to display each character. It starts at "1" and ends when it gets to the end of the string displaying a character at that particular position for each iteration of the loop. Note also that it creates 51 separate result sets. That means that 51 individual SELECTs were executed. Heh... that qualifies as "RBAR"!
Just like counting from 1 to 10, both the loop and the Tally table count from 1 to the length of the parameter. The Tally table is a direct replacement for the loop. Look at the following graphic. Both the loop and the Tally table do exactly the same thing except the Tally table only uses 1 SELECT and returns a single result set. The rows of the Tally table act as the counter except it's set based.
Notice, that almost didn't slow down at all. It's doing the same as the loop solution. The big difference is that it's doing it all in a single SELECT. It's using the existing rows in the Tally table as a counter. In essence, it' joining to the parameter at the character level... and that makes it very, very fast especially since the values of the Tally table are cached and the counters in the loop are not.
Ok, we now understand that a Tally table is a direct replacement for some loops... especially loops that count. We've also seen that the Tally table can single out certain characters much faster than using a loop. MUCH faster. Let's go all the way... let's split the parameter we've been using into the individual elements that are marked by the commas. Here's one way to do it with a loop except this time, I've cheated a bit to help the loop find the comma's using CHARINDEX instead of stepping through each character just to give it a fighting chance. I've also made the parameter more like what you'd get from a GUI... no leading or trailing commas...
Again, all this does is find a comma and "remembers" its position. Then it uses CharIndex to find the next comma and inserts what's between the commas into a table variable. It quits looping when it runs out of commas.
The details are in the comments. What I want to point out is that they're both very fast. 5 elements just isn't enough to determine a winner here. Let's make a "monster" parameter of 796 elements (last one will be blank) and see which one wins... here's the full code I ran...
The Tally table method wins for Duration, CPU time, and RowCounts. It's looses in Reads and that bothers some folks, but it shouldn't. Part of the reason the Tally table method works so well is because parts of the Tally table get cached and those are "Logical Reads" from memory. If you think these differences are small, multiply them by 10,000 or a million and see how much difference there is. If you want performance, stop using loops and start using a Tally table.
You've seen it... some poor slob posts that (s)he has a table and it has a CSV column in it. "How do you join to it?", they ask. The correct answer, of course, is to normalize the table so that there is no CSV column and everyone says so on the post. Heh, but then the recommendation that follows is usually "Write a cursor to normalize the table."
Once you've made the realization that joining to a Tally table is like creating a loop, you can do dozens of other things. Need to create a derived table with all the dates in a date range so you can outer join to it and get the SUM of 0 for dates you have no data for? That's easy with a Tally table...
Notice that the "%3" is used to provide the shift number based on the value of the Tally table. We're also making dates by multiplying the Tally table value by 8 hours which generates the dates/times from the base date. Like I said, there are dozens of uses... Just do a search on "Numbers Table" or "Tally Table".
This article isn't meant to show you every thing you can use a Tally table for. Rather, it was an introduction as to what a Tally table is and how it actually works to replace loops in a set based fashion. In many cases, the Tally table is a direct replacement for a loop that counts. In other cases, something a bit more exotic needs to happen in the form of a Cross Join with the Tally table like when you want to split a whole column of CSV's. I think that you'll find that no matter what the use is, a Tally table will always beat the pants off of a looping solution.
3a8082e126