• Coco Rogers (unregistered)

    FP!

  • (cs)

    This is the software that tracks your bank account folks...

  • DavidK (unregistered)

    Wow!

    Really... wow!

    Though scarily I can think of a situation in which this would run quicker than creating a second database table and performing a join: Cross database queries in Oracle where the databases are at different physical locations.

    In that scenario, a join across the two sites would really be nasty... so executing one simple piece of SQL at one end, returning the results, creating a nasty query like the above and executing locally would be quicker.

    And before someone says temporary tables... on older versions of Oracle temporary tables logged... and that means your log files really do start eating disk. In effect, temp tables weren't as temporary and discardable as you would desire. So the use of them was avoided quite heavily.

    That said... why not a regular table with queryId and custNum, and  populate it each time and truncate occasionally?

    Hmm... I dunno... I'm beyond trying to figure out why. Usually I can just about manage to, but even with knowledge of bizarre old Oracle quirks the above is still a WTF to be proud of.

  • (cs)
    Alex Papadimoulis:

    Today's code is an example of a fairly common pattern I see: a SQL query generator from some middleware generates an absurdly long "IN" predicate that includes the identifier from each row in the table. I'm not quite sure how one could manage to build a system that generates such a query, but I've always let these submissions slide anyway; they just didn't seem to be "enough." What makes Tim Hughes' find so impressive is that the "crack" C++ developers on his team were perfectly aware of how bad of an idea it is to make such absurdly long "IN" lists. Instead, they made sure to "OR" together their really long "IN" lists together. They even made sure that the WHERE clause wasn't too long, instead performing a "UNION" on multiple queries. The one part that they were having trouble with was getting their query to run fast, though ...

    <font color="#000099">SELECT</font> ORDR_NUM, ORDR_TTL, TAX_CD
    <font color="#000099">FROM</font> ORDR
    <font color="#000099">WHERE</font> ( CUST_NUM <font color="#000099">IN</font> (<font color="#990000">'110019'</font>,<font color="#990000">'110015'</font>,<font color="#990000">'110013'</font>,<font color="#990000">'110011'</font>,<font color="#990000">'110017'</font>,<font color="#990000">'110015'</font>,
    <font color="#990000">'110013'</font>,<font color="#990000">'110018'</font>,<font color="#990000">'110016'</font>,<font color="#990000">'110014'</font>,<font color="#990000">'110012'</font>,<font color="#990000">'120015'</font>,<font color="#990000">'120016'</font>,<font color="#990000">'150019'</font>,
    <font color="#990000">'150010'</font>,<font color="#990000">'150012'</font>,<font color="#990000">'150018'</font>,<font color="#990000">'150016'</font>,<font color="#990000">'150017'</font>,<font color="#990000">'150011'</font>,<font color="#990000">'150017'</font>,<font color="#990000">'150015'</font>,
    <font color="#006600">/* SNIP: few hundred lines */</font> ) <font color="#000099">OR</font> CUST_NUM <font color="#000099">IN</font> (<font color="#990000">'210017'</font>,<font color="#990000">'210013'</font>,<font color="#990000">'210011'</font>,<font color="#990000">'21V000'</font>,<font color="#990000">'22K018'</font>,<font color="#990000">'22W010'</font>,
    <font color="#990000">'22W018'</font>,<font color="#990000">'230057'</font>,<font color="#990000">'230237'</font>,<font color="#990000">'230502'</font>,<font color="#990000">'230311'</font>,<font color="#990000">'230314'</font>,<font color="#990000">'230318'</font>,<font color="#990000">'230357'</font>,
    <font color="#006600">/* SNIP: another few hundred lines */</font> ) <font color="#000099">OR</font> CUST_NUM <font color="#000099">IN</font> (<font color="#990000">'41K020'</font>,<font color="#990000">'41K016'</font>,<font color="#990000">'41K018'</font>,<font color="#990000">'41K011'</font>,<font color="#990000">'41K012'</font>,<font color="#990000">'41K018'</font>,
    <font color="#990000">'41K019'</font>,<font color="#990000">'41K015'</font>,<font color="#990000">'41K015'</font>,<font color="#990000">'41K018'</font>,<font color="#990000">'41K016'</font>,<font color="#990000">'41K014'</font>,<font color="#990000">'41K012'</font>,<font color="#990000">'41M012'</font>,
    <font color="#990000">'41M014'</font>,<font color="#990000">'41M013'</font>,<font color="#990000">'41M018'</font>,<font color="#990000">'41M013'</font>,<font color="#990000">'41M015'</font>,<font color="#990000">'41N014'</font>,<font color="#990000">'41S006'</font>,<font color="#990000">'41T003'</font>,
    <font color="#990000">'41T001'</font>,<font color="#990000">'41T001'</font>,<font color="#990000">'41T012'</font>,<font color="#990000">'41T010'</font>,<font color="#990000">'41T018'</font>,<font color="#990000">'41T015'</font>,<font color="#990000">'41V003'</font>,<font color="#990000">'41V002'</font>))
    <font color="#000099">UNION</font> <font color="#000099">SELECT</font> ORDR_NUM, ORDR_TTL, TAX_CD
    <font color="#000099">FROM</font> ORDR
    <font color="#000099">WHERE</font> ( CUST_NUM <font color="#000099">IN</font> (<font color="#990000">'610016'</font>,<font color="#990000">'610012'</font>,<font color="#990000">'610010'</font>,<font color="#990000">'610013'</font>,<font color="#990000">'610024'</font>,<font color="#990000">'610032'</font>,
    <font color="#990000">'610040'</font>,<font color="#990000">'610019'</font>,<font color="#990000">'610010'</font>,<font color="#990000">'610017'</font>,<font color="#990000">'610010'</font>,<font color="#990000">'610019'</font>,<font color="#990000">'610012'</font>,<font color="#990000">'610010'</font>,
    <font color="#990000">'610018'</font>,<font color="#990000">'610018'</font>,<font color="#990000">'610016'</font>,<font color="#990000">'610017'</font>,<font color="#990000">'611011'</font>,<font color="#990000">'611012'</font>,<font color="#990000">'612011'</font>,<font color="#990000">'612019'</font>,
    <font color="#006600">/* SNIP: a bunch more lines */</font> ) <font color="#000099">OR</font> CUST_NUM <font color="#000099">IN</font> ('
    <font color="#990000">'68E015'</font>,<font color="#990000">'68K000'</font>,<font color="#990000">'68K012'</font>,<font color="#990000">'68K019'</font>,<font color="#990000">'68K016'</font>,<font color="#990000">'68K012'</font>,<font color="#990000">'68K018'</font>,<font color="#990000">'68K014'</font>,
    <font color="#990000">'68K012'</font>,<font color="#990000">'68K010'</font>,<font color="#990000">'68K019'</font>,<font color="#990000">'68N019'</font>,<font color="#990000">'68T010'</font>,<font color="#990000">'68U015'</font>,<font color="#990000">'68V021'</font>,<font color="#990000">'68V039'</font>,
    <font color="#990000">'68V047'</font>,<font color="#990000">'68V054'</font>,<font color="#990000">'68V062'</font>,<font color="#990000">'68V070'</font>,<font color="#990000">'68V088'</font>,<font color="#990000">'68V096'</font>,<font color="#990000">'68V104'</font>,<font color="#990000">'68V112'</font>,
    <font color="#990000">'68V120'</font>,<font color="#990000">'68V138'</font>,<font color="#990000">'68V146'</font>,<font color="#990000">'68V153'</font>,<font color="#990000">'68V017'</font>,<font color="#990000">'68V018'</font>,<font color="#990000">'68V011'</font>,<font color="#990000">'68V016'</font>))
    <font color="#000099">UNION</font> <font color="#006600">/* You probably get the pattern by now */</font> <font color="#000099">UNION</font> <font color="#000099">SELECT</font> ORDR_NUM, ORDR_TTL, TAX_CD
    <font color="#000099">FROM</font> ORDR
    <font color="#000099">WHERE</font> ( CUST_NUM <font color="#000099">IN</font> (<font color="#990000">'920001'</font>,<font color="#990000">'920002'</font>,<font color="#990000">'920003'</font>,<font color="#990000">'920000'</font>,<font color="#990000">'920008'</font>,<font color="#990000">'920007'</font>,
    <font color="#990000">'920015'</font>,<font color="#990000">'920010'</font>,<font color="#990000">'920012'</font>,<font color="#990000">'920018'</font>,<font color="#990000">'920013'</font>,<font color="#990000">'920019'</font>,<font color="#990000">'920019'</font>,<font color="#990000">'920012'</font>,
    <font color="#990000">'920013'</font>,<font color="#990000">'920012'</font>))

    I Take this case as a layer
    this code was made by another program. Take some tiem to look at it closer, for instance you have
    <font color="#990000">'920019'</font>,<font color="#990000">'920019'
    </font><font color="#990000">110015 is also repeated</font><font color="#990000">

    He might get these codes from another BD, then there's no other way to
    do this :(

    He admits to be guilty of not checking the duplicated values

    </font><font color="#990000"></font>
    <font color="#990000">
    </font>

  • (cs) in reply to ZeoS

    <FONT face=Verdana size=2>Sweet, I just leaned a new skill. I never would have thought of it.</FONT>

  • sLayer (unregistered)

    Oracle has this (quite reasonable) limitation that it only allows 999 elements in the IN clause. To get around that one would use IN (1, 2,....999) OR (1000,1001,...). That said, before doing stuff like this one SHOULD think whether this is a good idea in the first place.

    The sad part is that the reason I know this limitation is because some 'clever' developers decided to do exactly the same thing in one of our projects :(

  • (cs)

    Another good example of where the problem isn't what it seems ...

    The problem is not that there are better ways to pass in a list of hundreds of keys as parameters; it is the the fact that they are passing in lists of hundreds of keys as parameters in the first place.

    A typical "requirement" on many jobs I've done goes like this: 

    Customer: "I need a report that shows salaries for these employees." (hands a list of 20 employe numbers)

    Me: "Why these 20 employees?"

    Customer: " those are the ones I want to see!"

    Me: "Yes, but what is it about these 20 employees that makes you want to see them on this particular report? What do they have in common?"

    Customer: "Are you dense? What they have in common is that I want the report to show their salaries!"

    Me: "Yes, I understand that ... why did you choose this list of 20? Will this list ever change?  What makes it change?"

    Customer: "I don't follow.  I just want to see the salaries of everyone in the Finance department on the report, and this is the list!  Also, I don't want employees that have been terminated, of course."

    Me: "So you want a report that shows the salaries of active employees in the Finance department? "

    Customer: "Yes! Isn't that what I said?  Also, we will eventually reports for the other departments as well."

    Me: "So, maybe a report that *prompts* for a department, and from there, shows the salaries of the active employees in that department?"

    Customer: "Yes! Isn't that what I've been saying?"

    The problem is the inability of the customer to say what he really needs, and the inability of the programmer to sometimes stop and think "there must be a way of doing this without hard-coding in 200 employee numbers!"  I am amazed at how often conversations like this occur and how often these talks are handled poorly on both ends.

    Often, these requirements (when finally stated logically and clearly) demonstrate the need for additional attributes to be stored on the tables that aren't there; if you constantly need to list of a set of rows that have something in common for any purpose, those rows should have a common attribute.   Your data should be driving your logic; you shouldn't be embedding data in your code.

  • (cs)

    Where I work, one of our products generates a worklist that could potentially have a very long list of IDs in the IN clause. None of our clients ever complained because they didn't have a lot of items on their worklist but we had when running the application against our Oracle test database because the IN clause exceeded 255 values. I ended up fixing this problem by having the application create a temporary table with all the IDs that were needed and joining the main query with the IDs in the temp table. After that it would drop the temporary table.
    Maybe that's why he has all of these ORs in the query. Perhaps he was getting the same database error and this was the workaround.

    However, our query didn't have a UNION on multiple queries. I can't figure out why they needed to do that.

    So, what is a good solution for a situation where you need to select rows from a table and there are a large number of IDs that need to be included in the IN clause and these IDs are not known until runtime?

    • Dan


  • Bill (unregistered) in reply to Jeff S

    Your points are valid, but irrelevant to this WTF. There's no way that someone handed a programmer a paper with thousands of customer numbers and had them write a query against it.

    The obvious choice has been stated, poor middleware writing queries like this.

    b

  • (cs) in reply to Jeff S
    Jeff S:

    Another good example of where the problem isn't what it seems ...

    The problem is not that there are better ways to pass in a list of hundreds of keys as parameters; it is the the fact that they are passing in lists of hundreds of keys as parameters in the first place.

    A typical "requirement" on many jobs I've done goes like this: 

    Customer: "I need a report that shows salaries for these employees." (hands a list of 20 employe numbers)

    Me: "Why these 20 employees?"

    Customer: " those are the ones I want to see!"

    Me: "Yes, but what is it about these 20 employees that makes you want to see them on this particular report? What do they have in common?"

    Customer: "Are you dense? What they have in common is that I want the report to show their salaries!"

    Me: "Yes, I understand that ... why did you choose this list of 20? Will this list ever change?  What makes it change?"

    Customer: "I don't follow.  I just want to see the salaries of everyone in the Finance department on the report, and this is the list!  Also, I don't want employees that have been terminated, of course."

    Me: "So you want a report that shows the salaries of active employees in the Finance department? "

    Customer: "Yes! Isn't that what I said?  Also, we will eventually reports for the other departments as well."

    Me: "So, maybe a report that *prompts* for a department, and from there, shows the salaries of the active employees in that department?"

    Customer: "Yes! Isn't that what I've been saying?"

    The problem is the inability of the customer to say what he really needs, and the inability of the programmer to sometimes stop and think "there must be a way of doing this without hard-coding in 200 employee numbers!"  I am amazed at how often conversations like this occur and how often these talks are handled poorly on both ends.

    Often, these requirements (when finally stated logically and clearly) demonstrate the need for additional attributes to be stored on the tables that aren't there; if you constantly need to list of a set of rows that have something in common for any purpose, those rows should have a common attribute.   Your data should be driving your logic; you shouldn't be embedding data in your code.



    You make a very good point. This kind of dialog between the vendor and customer is very important. Once you understand why the customer needs something, it is much easier to figure out the best way of doing it or come up with better alternatives to just blindly hardcoding in 20 employee IDs.
  • (cs) in reply to Jeff S
    Jeff S:
    Another good example of where the problem isn't what it seems ...

    The problem is not that there are better ways to pass in a list of hundreds of keys as parameters; it is the the fact that they are passing in lists of hundreds of keys as parameters in the first place.


    And given the incremental way that these requests can grow, you can have such a situation creep up on you.  You did not mean to write such code, but you are like the frog in the pot of heating water.

    A typical "requirement" on many jobs I've done goes like this:

    BTDT.  I am not alone.

    Customer: "I don't follow.  I just want to see the salaries of everyone in the Finance department on the report, and this is the list!  Also, I don't want employees that have been terminated, of course."

    Me: "So you want a report that shows the salaries of active employees in the Finance department? "

    Customer: "Yes! Isn't that what I said?  Also, we will eventually reports for the other departments as well."

    Me: "So, maybe a report that *prompts* for a department, and from there, shows the salaries of the active employees in that department?"

    Customer: "Yes! Isn't that what I've been saying?"

    The problem is the inability of the customer to say what he really needs, and the inability of the programmer to sometimes stop and think "there must be a way of doing this without hard-coding in 200 employee numbers!"  I am amazed at how often conversations like this occur and how often these talks are handled poorly on both ends.


    In actuality, you probably have to prompt the customer for whether it should be only active employees and whether this report will be needed for different departments as well.  Do not forget the order of the report.  Employee number, employee name, salary, or something else?

    Often, these requirements (when finally stated logically and clearly) demonstrate the need for additional attributes to be stored on the tables that aren't there; if you constantly need to list of a set of rows that have something in common for any purpose, those rows should have a common attribute.   Your data should be driving your logic; you shouldn't be embedding data in your code.

    Quite.  Again, the frog in the pot is too easy.

    Sincerely,

    Gene Wirchenko

  • (cs)

    The problem, of course, is that there are LETTERS mixed in with the NUMBERS:

    <font>'68E015'


    </font>

    So, what is the solution, SQL people? 
    A temporary / session table, which you fill with the IDs you want, and join on that?
    Returning all the rows is not an option, for large tables...


  • (cs) in reply to Anonymoose
    Anonymoose:
    The problem, of course, is that there are LETTERS mixed in with the NUMBERS:
    <FONT size=+0>'68E015'


    </FONT>


    So, what is the solution, SQL people? 
    A temporary / session table, which you fill with the IDs you want, and join on that?
    Returning all the rows is not an option, for large tables...


    The solution is to store common attributes about these entities in the data and to select them based on those attributes w/o explicitly listing them all out. 

    Unless your app displays a long list of 10,000 items, and forces the user to manually click each one to select them, and the user is just randomly picking from that list without any rhyme or reason or pattern, you should never have to implement a SQL statement like this.

  • (cs)

    You see, this is why us crack C++ developers are smarter than anyone else. Only an idiot would work with something as brain-dead SQL. I mean, didn't it occur to the geniuses that invented SQL that sometimes you want to just pull all records from the table without any criteria? I mean is it just too blindingly obvious that SQL should let you just leave out the Where block to accomplish this? But, no, I get stuck with this so-called "language" and have to spend all day dividing a huge In list into manageable bites. And now everytime they add a customer, I have to change this code to add a new Cust_Num. What a WTF.

    Well, at least the second step is sensible. That's where I use C++ to copy the query result into a link list so I can loop through and sum the total orders, which is all they really want anyway.

    If SQL were a real language, it would have a Sum function.

    --Rank

  • (cs) in reply to Bill

    Anonymous:
    Your points are valid, but irrelevant to this WTF. There's no way that someone handed a programmer a paper with thousands of customer numbers and had them write a query against it.

    The obvious choice has been stated, poor middleware writing queries like this.

    b

    It is completely relevant to this WTF. 

    If the specs are that a user must manually click on 1000's of completely unrelated, random customer id's and pass them to the next layer, then the problem isn't the middleware, it's the specs.

  • Frakkle (unregistered)

    I've been guilty of this in the past, though not by my choice.

    The situation I've done this is when I needed to make a selection against a database based on the records contained in a geographical area chosen by a user.

    The mapping software would return their IDs, which was the only key that was used to hook it to the database.  I then had to query the database for all of the IDs.  I would have preferred to use a geospatial database, which would allow me to tie the spatial records directly to the attribute records, but for this client, that was not approved software.  So a giant "IN" clause was necessary, and rather unavoidable.

    There are scenarios where you have no real choice.  And in all honesty, it didn't perform poorly.  Even when it was fetching some 10,000 records.

    I do not know if creating a temporary table and inserting those records and then doing a join would be faster, I haven't benchmarked it.

  • (cs)

    Um....did nobody ever hear of the EXISTS command?? The simple fact they used IN is a WTF to me...

  • Frakkle (unregistered) in reply to OMG

    OMG:
    Um....did nobody ever hear of the EXISTS command?? The simple fact they used IN is a WTF to me...

    Aside from EXISTS' behavior being somewhat RDBMS-specific, I'm not sure how it could be used to make this query cleaner?  Explain.

  • my head hurts (unregistered)

    I've unfortunately seen this code, and can explain why they did this.

    In Oracle, there's some magic number of IN variables, around 38, after which it will cease to use an index, and will always use a table scan.

    Believe it or not, code like the above, as ugly as it is, will actually make use of the index!

    If I see someone write code like this I commend them for knowing that this issue exists, then take away their lunch money until they fix it the right way.

  • (cs) in reply to Frakkle
    Anonymous:

    I've been guilty of this in the past, though not by my choice.

    The situation I've done this is when I needed to make a selection against a database based on the records contained in a geographical area chosen by a user.

    The mapping software would return their IDs, which was the only key that was used to hook it to the database.  I then had to query the database for all of the IDs.  I would have preferred to use a geospatial database, which would allow me to tie the spatial records directly to the attribute records, but for this client, that was not approved software.  So a giant "IN" clause was necessary, and rather unavoidable.

    There are scenarios where you have no real choice.  And in all honesty, it didn't perform poorly.  Even when it was fetching some 10,000 records.

    I do not know if creating a temporary table and inserting those records and then doing a join would be faster, I haven't benchmarked it.



    it´s Worst!, the number of comparisons are the same, and you have to insert the values!

    You have #orderRows*#clientsToCompare tests in both cases. We can asume that the 2nd table will be indexed, but we'll have a slower insertion
  • (cs) in reply to OMG

    OMG:
    Um....did nobody ever hear of the EXISTS command?? The simple fact they used IN is a WTF to me...

    How exactly would using EXISTS make this "better" ?

  • (cs) in reply to Anonymoose
    Anonymoose:
    The problem, of course, is that there are LETTERS mixed in with the NUMBERS:
    <FONT size=+0>'68E015'


    </FONT>


    So, what is the solution, SQL people? 
    A temporary / session table, which you fill with the IDs you want, and join on that?
    Returning all the rows is not an option, for large tables...


    In MS SQL, I prefer to use WHERE CHARINDEX('|' + CONVERT(varchar, ColumnName) + '|', @list) > 0 where @list is varchar(8000).  You would pass @list with values like '|100|266|174|' or '|TX|AK|MO|'.  This gets around the temp table issues and the in limitations.

  • (cs) in reply to JohnO
    JohnO:
    Anonymoose:
    The problem, of course, is that there are LETTERS mixed in with the NUMBERS:
    <FONT size=+0>'68E015'


    </FONT>


    So, what is the solution, SQL people? 
    A temporary / session table, which you fill with the IDs you want, and join on that?
    Returning all the rows is not an option, for large tables...


    In MS SQL, I prefer to use WHERE CHARINDEX('|' + CONVERT(varchar, ColumnName) + '|', @list) > 0 where @list is varchar(8000).  You would pass @list with values like '|100|266|174|' or '|TX|AK|MO|'.  This gets around the temp table issues and the in limitations.

    That SELECT will be less efficient than a very long IN() clause; every row needs to be scanned to evaluate the WHERE condition.

    If you have to pass a list of values to a SELECT, the most efficient way (unless you are querying a very small table or a table with no indexes) is to populate a temp table or table variable with what you need and to join to that.

  • OneMHz (unregistered) in reply to sLayer
    Anonymous:
    Oracle has this (quite reasonable) limitation that it only allows 999 elements in the IN clause. To get around that one would use IN (1, 2,....999) OR (1000,1001,...). That said, before doing stuff like this one SHOULD think whether this is a good idea in the first place.

    The sad part is that the reason I know this limitation is because some 'clever' developers decided to do exactly the same thing in one of our projects :(


    It's sad, but I've run into that one too.  It was an unlikely situation that only came up during stress testing.  It took me forever to figure out what was happening because I didn't know about this limitation before I found the issue.  I took it as proof outsourcing is a bad idea.
  • Anon (unregistered) in reply to Frakkle

    Passing in the IDs as an XML document would be another way.

  • (cs) in reply to ZeoS
    ZeoS:
    Anonymous:

    I've been guilty of this in the past, though not by my choice.

    The situation I've done this is when I needed to make a selection against a database based on the records contained in a geographical area chosen by a user.

    The mapping software would return their IDs, which was the only key that was used to hook it to the database.  I then had to query the database for all of the IDs.  I would have preferred to use a geospatial database, which would allow me to tie the spatial records directly to the attribute records, but for this client, that was not approved software.  So a giant "IN" clause was necessary, and rather unavoidable.

    There are scenarios where you have no real choice.  And in all honesty, it didn't perform poorly.  Even when it was fetching some 10,000 records.

    I do not know if creating a temporary table and inserting those records and then doing a join would be faster, I haven't benchmarked it.



    it´s Worst!, the number of comparisons are the same, and you have to insert the values!

    You have #orderRows*#clientsToCompare tests in both cases. We can asume that the 2nd table will be indexed, but we'll have a slower insertion

    Wrong; your assumption completely ignores indexes.

    if your table is properly indexed and it is sufficiently large so that efficiency is important, adding rows to a temp table (or table variable) and then joining to that will be much, much more efficient. 

  • Pope (unregistered) in reply to Rank Amateur
    Rank Amateur:

    You see, this is why us crack C++ developers are smarter than anyone else. Only an idiot would work with something as brain-dead SQL. I mean, didn't it occur to the geniuses that invented SQL that sometimes you want to just pull all records from the table without any criteria? I mean is it just too blindingly obvious that SQL should let you just leave out the Where block to accomplish this? But, no, I get stuck with this so-called "language" and have to spend all day dividing a huge In list into manageable bites. And now everytime they add a customer, I have to change this code to add a new Cust_Num. What a WTF.

    Well, at least the second step is sensible. That's where I use C++ to copy the query result into a link list so I can loop through and sum the total orders, which is all they really want anyway.

    If SQL were a real language, it would have a Sum function.

    --Rank

    Excellent rant, except for one thing: you've obviously never used SQL before.

    This WTF make me shudder... mainly because I actually programmed something like that in one of my apps directly after I learned about information_schema waaaaaay back.  "It auto-magically pulls everything from every table guys!  Look!"  I was really after a way to abuse every resource available. 

  • (cs) in reply to Jeff S
    Jeff S:
    JohnO:
    Anonymoose:
    The problem, of course, is that there are LETTERS mixed in with the NUMBERS:
    <FONT size=+0>'68E015'


    </FONT>


    So, what is the solution, SQL people? 
    A temporary / session table, which you fill with the IDs you want, and join on that?
    Returning all the rows is not an option, for large tables...


    In MS SQL, I prefer to use WHERE CHARINDEX('|' + CONVERT(varchar, ColumnName) + '|', @list) > 0 where @list is varchar(8000).  You would pass @list with values like '|100|266|174|' or '|TX|AK|MO|'.  This gets around the temp table issues and the in limitations.

    That SELECT will be less efficient than a very long IN() clause; every row needs to be scanned to evaluate the WHERE condition.

    If you have to pass a list of values to a SELECT, the most efficient way (unless you are querying a very small table or a table with no indexes) is to populate a temp table or table variable with what you need and to join to that.

    You are assuming an index on the column in question.

  • (cs) in reply to JohnO

    You can also check for one item and optimize for that or unwind the string into a temp table in the stored procedure.

  • (cs) in reply to JohnO
    JohnO:
    Jeff S:
    JohnO:
    Anonymoose:
    The problem, of course, is that there are LETTERS mixed in with the NUMBERS:
    <FONT size=+0>'68E015'


    </FONT>


    So, what is the solution, SQL people? 
    A temporary / session table, which you fill with the IDs you want, and join on that?
    Returning all the rows is not an option, for large tables...


    In MS SQL, I prefer to use WHERE CHARINDEX('|' + CONVERT(varchar, ColumnName) + '|', @list) > 0 where @list is varchar(8000).  You would pass @list with values like '|100|266|174|' or '|TX|AK|MO|'.  This gets around the temp table issues and the in limitations.

    That SELECT will be less efficient than a very long IN() clause; every row needs to be scanned to evaluate the WHERE condition.

    If you have to pass a list of values to a SELECT, the most efficient way (unless you are querying a very small table or a table with no indexes) is to populate a temp table or table variable with what you need and to join to that.

    You are assuming an index on the column in question.

    Yes.

  • (cs) in reply to Pope
    Anonymous:
    Rank Amateur:

    You see, this is why us crack C++ developers are smarter than anyone else. Only an idiot would work with something as brain-dead SQL. I mean, didn't it occur to the geniuses that invented SQL that sometimes you want to just pull all records from the table without any criteria? I mean is it just too blindingly obvious that SQL should let you just leave out the Where block to accomplish this? But, no, I get stuck with this so-called "language" and have to spend all day dividing a huge In list into manageable bites. And now everytime they add a customer, I have to change this code to add a new Cust_Num. What a WTF.

    Well, at least the second step is sensible. That's where I use C++ to copy the query result into a link list so I can loop through and sum the total orders, which is all they really want anyway.

    If SQL were a real language, it would have a Sum function.

    --Rank

    Excellent rant, except for one thing: you've obviously never used SQL before.

    This WTF make me shudder... mainly because I actually programmed something like that in one of my apps directly after I learned about information_schema waaaaaay back.  "It auto-magically pulls everything from every table guys!  Look!"  I was really after a way to abuse every resource available. 

    I think he was being sarcastic .... read it again.

  • Frakkle (unregistered) in reply to Jeff S
    Jeff S:
    ZeoS:
    Anonymous:

    I've been guilty of this in the past, though not by my choice.

    The situation I've done this is when I needed to make a selection against a database based on the records contained in a geographical area chosen by a user.

    The mapping software would return their IDs, which was the only key that was used to hook it to the database.  I then had to query the database for all of the IDs.  I would have preferred to use a geospatial database, which would allow me to tie the spatial records directly to the attribute records, but for this client, that was not approved software.  So a giant "IN" clause was necessary, and rather unavoidable.

    There are scenarios where you have no real choice.  And in all honesty, it didn't perform poorly.  Even when it was fetching some 10,000 records.

    I do not know if creating a temporary table and inserting those records and then doing a join would be faster, I haven't benchmarked it.



    it´s Worst!, the number of comparisons are the same, and you have to insert the values!

    You have #orderRows*#clientsToCompare tests in both cases. We can asume that the 2nd table will be indexed, but we'll have a slower insertion

    Wrong; your assumption completely ignores indexes.

    if your table is properly indexed and it is sufficiently large so that efficiency is important, adding rows to a temp table (or table variable) and then joining to that will be much, much more efficient. 

    I'll have to try that.  I primarily use MySQL -- anyone have experience with temp tables using MySQL 5.0 and its performance?  I have never used them before in it.  I have in SQL Server.

  • Stingray (unregistered) in reply to Jeff S
    Jeff S:

    Me: "So, maybe a report that *prompts* for a department, and from there, shows the salaries of the active employees in that department?"

    Customer: "Yes! Isn't that what I've been saying?"



    So I'll assume that, for the sake of brevity, you omitted the requirement where only certain people could access the report, using some sort of protection mechanism? At least where I work, employee salaries are privileged confidential information that is only available to the people who need to know it to do their job :)
  • Pope (unregistered) in reply to Jeff S
    Jeff S:
    Anonymous:
    Rank Amateur:

    You see, this is why us crack C++ developers are smarter than anyone else. Only an idiot would work with something as brain-dead SQL. I mean, didn't it occur to the geniuses that invented SQL that sometimes you want to just pull all records from the table without any criteria? I mean is it just too blindingly obvious that SQL should let you just leave out the Where block to accomplish this? But, no, I get stuck with this so-called "language" and have to spend all day dividing a huge In list into manageable bites. And now everytime they add a customer, I have to change this code to add a new Cust_Num. What a WTF.

    Well, at least the second step is sensible. That's where I use C++ to copy the query result into a link list so I can loop through and sum the total orders, which is all they really want anyway.

    If SQL were a real language, it would have a Sum function.

    --Rank

    Excellent rant, except for one thing: you've obviously never used SQL before.

    This WTF make me shudder... mainly because I actually programmed something like that in one of my apps directly after I learned about information_schema waaaaaay back.  "It auto-magically pulls everything from every table guys!  Look!"  I was really after a way to abuse every resource available. 

    I think he was being sarcastic .... read it again.

    Caught again!  Damn my autism! 

  • jbange (unregistered) in reply to Jeff S
    Jeff S:

    The solution is to store common attributes about these entities in the data and to select them based on those attributes w/o explicitly listing them all out. 

    Unless your app displays a long list of 10,000 items, and forces the user to manually click each one to select them, and the user is just randomly picking from that list without any rhyme or reason or pattern, you should never have to implement a SQL statement like this.



    Indeed, this point bears repreating: What criteria generated that list of numbers, and why is that information not being used to select the appropriate data from the database? Even if the list was generated by 100 telemarketing monkeys clicking the "sucker" button on their call sheet, that clcik should be updating the "sucker" flag in the database.
  • (cs) in reply to Jeff S
    Jeff S:
    ZeoS:
    Anonymous:

    I've been guilty of this in the past, though not by my choice.

    The situation I've done this is when I needed to make a selection against a database based on the records contained in a geographical area chosen by a user.

    The mapping software would return their IDs, which was the only key that was used to hook it to the database.  I then had to query the database for all of the IDs.  I would have preferred to use a geospatial database, which would allow me to tie the spatial records directly to the attribute records, but for this client, that was not approved software.  So a giant "IN" clause was necessary, and rather unavoidable.

    There are scenarios where you have no real choice.  And in all honesty, it didn't perform poorly.  Even when it was fetching some 10,000 records.

    I do not know if creating a temporary table and inserting those records and then doing a join would be faster, I haven't benchmarked it.



    it´s Worst!, the number of comparisons are the same, and you have to insert the values!

    You have #orderRows*#clientsToCompare tests in both cases. We can asume that the 2nd table will be indexed, but we'll have a slower insertion

    Wrong; your assumption completely ignores indexes.

    if your table is properly indexed and it is sufficiently large so that efficiency is important, adding rows to a temp table (or table variable) and then joining to that will be much, much more efficient. 


    sometimes you can't properly index for every query you need, maybe this query runes once a month so if it takes 3 minutes tu run theres no problem...
  • (cs) in reply to Stingray
    Anonymous:
    Jeff S:

    Me: "So, maybe a report that *prompts* for a department, and from there, shows the salaries of the active employees in that department?"

    Customer: "Yes! Isn't that what I've been saying?"



    So I'll assume that, for the sake of brevity, you omitted the requirement where only certain people could access the report, using some sort of protection mechanism? At least where I work, employee salaries are privileged confidential information that is only available to the people who need to know it to do their job :)

    Point completely missed !  But thanks for playing!

    (I bet you're the guy who interrupts with pointless questions when someone is giving an example ...  you know, if someone asks "I ride a train travelling 100MPH from point A to point B, 300 miles away. How long did it take?" you'd respond with  "How did you get on the train? Did you but a ticket? Where did you sit?  What was the conductor's name?")

  • (cs)

    <FONT size=2>I think I hear their database screaming in agony from here...</FONT>

    <FONT size=2>[pi]</FONT>

  • Your name (unregistered) in reply to MikeB

    SELECT SQL_STMT
    FROM TDWTF
    WHERE I LIKE

  • (cs) in reply to BradBrening
    BradBrening:

    I think I hear their database screaming in agony from here...

    [pi]

    No pizza for me.

  • (cs)

    I'll admit to falling into this trap once, when handling data synchronisation from remote clients. The magic IN clause got its keys from the sync message, so it was like:

    "Give me the rows that were updated after date X, but not those in (keys of rows that were just inserted)"

    The real solution would've been to use JavaScript...

  • Pope (unregistered)

    If you're building a huge dynamic SQL query like this WTF (I'm assuming), I've always wondered if it would be better to do an IN with a bunch of parameters, or to do several hundred Where CUST_NUM = 'blah1' OR CUST_NUM = 'blah2' OR CUST_NUM ='blah3' ... ad naseum. 

    For instance, to return a group of stores in a company or large franchise.  Which would be less resource intensive/return results more quickly?

    There was a situation I was placed in a couple of years ago, I had to make this choice and vied for the latter... either that or completely reprogram their product, which I didn't have time for.

  • (cs) in reply to Jeff S
    Jeff S:
    JohnO:
    Jeff S:
    JohnO:
    Anonymoose:
    The problem, of course, is that there are LETTERS mixed in with the NUMBERS:
    <FONT size=+0>'68E015'


    </FONT>


    So, what is the solution, SQL people? 
    A temporary / session table, which you fill with the IDs you want, and join on that?
    Returning all the rows is not an option, for large tables...


    In MS SQL, I prefer to use WHERE CHARINDEX('|' + CONVERT(varchar, ColumnName) + '|', @list) > 0 where @list is varchar(8000).  You would pass @list with values like '|100|266|174|' or '|TX|AK|MO|'.  This gets around the temp table issues and the in limitations.

    That SELECT will be less efficient than a very long IN() clause; every row needs to be scanned to evaluate the WHERE condition.

    If you have to pass a list of values to a SELECT, the most efficient way (unless you are querying a very small table or a table with no indexes) is to populate a temp table or table variable with what you need and to join to that.

    You are assuming an index on the column in question.

    Yes.

    I read what you highlighted the first time -- a table could have many indexes, just not one on the column you have criteria for.

  • (cs)

    As if that whole thing wasn't bad enough, using "UNION" instead of "UNION ALL" further slows it down.

  • Joel (unregistered) in reply to ferrengi

    "So, what is a good solution for a situation where you need to select rows from a table and there are a large number of IDs that need to be included in the IN clause and these IDs are not known until runtime?"

    Two words: bind variables.

    Another problem with this particular WTF is that the ids are hard-coded into the SQL text, instead of being bound to SQL placeholders of some sort. Virtually every modern RDBMS supports placeholders or bind variables, and for good reason. With placeholders (and appropriate client-side programming) the db engine only parses and optimizes the query once. Without bind variables, the smallest change to the parameters will force the db to reparse and re-optimize the query every time it's executed. At best, it's extra performance overhead that's easily avoided. At worst, it can drag a db server to its knees, as it attempts to cache many nearly identical query plans, eating up resources, forcing other queries out of the cache, etc. If someone tried crap like this on the dbs I'm responsible for, they'd find their modules locked out immediately.

  • (cs)

    There is never a good reason for this sort of code. Ever.

    Even if you, god forbid, are taking a list of IDs from some user input in some sick twisted fashion, you would be better off loading those records into a temp table and joining that with the table you're selecting from. Because if the column you're joining with is indexed, it'll be a lot faster than this, and if it's not indexed, well, it sure as hell can't be any slower.

    And, of course, there's rarely a good reason to take a list of arbitrary IDs from the user. Usually they're selecting based on something else, like department, or salary, or something that these users have in common. Even if they want multiple sets of commonalities, you can work it into a complex query of some type.

    And if this is really a list of every ID in the table, then the programmer needs to be shot. In the gut. So he can feel all the pain we had to feel from reading this code. [:D]

  • (cs) in reply to ammoQ

    ammoQ:
    As if that whole thing wasn't bad enough, using "UNION" instead of "UNION ALL" further slows it down.

    That is a GREAT point ...  Probably 9 times of 10 that UNION is used, UNION ALL should be used instead.

  • (cs) in reply to JohnO
    JohnO:

    In MS SQL, I prefer to use WHERE CHARINDEX('|' + CONVERT(varchar, ColumnName) + '|', @list) > 0 where @list is varchar(8000).  You would pass @list with values like '|100|266|174|' or '|TX|AK|MO|'.  This gets around the temp table issues and the in limitations.



    Tell me that was a joke. PLEASE.
  • (cs) in reply to Jeff S
    Jeff S:

    ammoQ:
    As if that whole thing wasn't bad enough, using "UNION" instead of "UNION ALL" further slows it down.

    That is a GREAT point ...  Probably 9 times of 10 that UNION is used, UNION ALL should be used instead.



    Good guess on the ratio;-) I've just counted on a project where I've recently checked all unions; it's 398 of 433 times.
  • (cs)

    One GUI pattern comes to my mind: a database grid, full of records; one column is a checkbox where the user can select rows he wants to process; and then there is a button labeled "select all" that marks all checkboxes. Implement that in a straight-forward-fashion and it looks like todays WTF.

Leave a comment on “Everything ... and I mean Everything”

Log In or post as a guest

Replying to comment #:

« Return to Article