views:

379

answers:

4

I have a field in a table recipes that has been inserted using mysql_real_escape_string, I want to count the number of line breaks in that field and order the records using this number.

p.s. the field is called Ingredients.

Thanks everyone

+4  A: 

This would do it:

SELECT *, LENGTH(Ingredients) - LENGTH(REPLACE(Ingredients, '\n', '')) as Count
FROM Recipes
ORDER BY Count DESC

The way I am getting the amount of linebreaks is a bit of a hack, however, and I don't think there's a better way. I would recommend keeping a column that has the amount of linebreaks if performance is a huge issue. For medium-sized data sets, though, I think the above should be fine.

If you wanted to have a cache column as described above, you would do:

UPDATE
    Recipes
SET
    IngredientAmount = LENGTH(Ingredients) - LENGTH(REPLACE(Ingredients, '\n', ''))

After that, whenever you are updating/inserting a new row, you could calculate the amounts (probably with PHP) and fill in this column before-hand. Or, if you're into that sort of thing, try out triggers.

Paolo Bergantino
I'm afraid this doesn't meet the requirements - you're just ordering by the length of the string, not the number of line breaks.
edoloughlin
Are you really looking at what the query is doing? :) Count is going to be the difference between the length of the string and the length of the string without line breaks. So if I have the string "abc\ndef" which has length of 7, and I subtract it from abcdef (which is what REPLACE('abc\ndef', '\n', '') would give me) the string has a length of 6, so the difference is 1, which is the amount of linebreaks in the string... this works and is tested.
Paolo Bergantino
Thanks that worked a treat! How would I echo the resulting COUNT from a field in the resulting rows?
bluedaniel
The row is named 'Count', so whatever your dataset's way of getting a row named 'Count' would be :) maybe $row['count'] if you're using mysql_* functions.
Paolo Bergantino
fantastic, if you would like to see the results thus far http://www.whatcouldicook.com/recipes/ingredients
bluedaniel
A: 

You're going a bit beyond the capabilities and intent of SQL here. You could write a stored procedure to scan the string and return the number and then use this in your query.

However, I think you should revisit the design of whatever is inserting the Ingredients so that you avoid searching strings in of every row whenever you do this query. Add a 'num_linebreaks' column, calculate the number of line breaks and set this column when you're adding the Indgredients.

If you've no control over the app that's doing the insertion, then you could use a stored procedure to update num_linebreaks based on a trigger.

edoloughlin
Is this a memory intrusive query?
bluedaniel
Yes. Just think about how it would be executed. You're scanning a string in every row, potentially for every time you query the table (I don't know how or if MySQL optimises the results of string functions).
edoloughlin
how would a php function like that look like? I know where all the triggers are.
bluedaniel
Can't answer that; PHP makes my head hurt :)
edoloughlin
has stackoverflow been stumped?! Seriously this place has been an unbelievable help, I wish this website all the success in the world
bluedaniel
A: 

Got it thanks, the php code looks like:

$check = explode("\r\n", $_POST['ingredients']); 
$lines = count($check);

So how could I update all the information in the table so Ingred_count based on field Ingredients in one fellow swoop for previous records?

bluedaniel
+2  A: 

I'm assuming a lot here, but from what I'm reading in your post, you could change your database structure a little bit, and both solve this problem and open your dataset up to more interesting uses.

If you separate ingredients into its own table, and use a linking table to index which ingredients occur in which recipes, it'll be much easier to be creative with data manipulation. It becomes easier to count ingredients per recipe, to find similarities in recipes, to search for recipes containing sets of ingredients, etc. also your data would be more normalized and smaller. (storing one global list of all ingredients vs. storing a set for each recipe)

If you're using a single text entry field to enter ingredients for a recipe now, you could do something like break up that input by lines and use each line as an ingredient when saving to the database. You can use something like PHP's built-in levenshtein() or similar_text() functions to deal with misspelled ingredient names and keep the data as normalized as possbile without having to hand-groom your [users'] data entry too much.

This is just a suggestion, take it as you like.

ithcy
yeah I do understand that and I've been wrestling with this for a while, the problem is the huge variety of ingredients they could enter, not just tomato but sun blushed tomato etc, as well as the quantity.Perhaps the process needs to be normalized and streamlined in the near future but for the moment it works.I want it to still be very open and natural process when it comes to data entry and dont want people to feel restricted like some other websites do, I think I can manipulate the data with enough php
bluedaniel
Its actually the quantity aspect that I would most struggle with
bluedaniel
oh, now i understand what you mean by quantity. there are a few ways to approach that, and i'm not a database master, but it seems to me you could just have a quantity column in your linking table. column 1 is ingredient id, column 2 is recipe id, column 3 is quantity.
ithcy