Sunday, April 17, 2011

How to specify notificationSender? (Cocoa Touch)

All of the examples I can find of setting up a notification look like this:

[nc addObserver:self selector:@selector(keyboardWillShow:) name: UIKeyboardWillShowNotification object:nil];

where object is always being set to nil. object is the notificationSender, which is defined thusly in the documentation:

"The object whose notifications the observer wants to receive; that is, only notifications sent by this sender are delivered to the observer. When nil, the notification center doesn’t use a notification’s sender to decide whether to deliver it to the observer."

I want to receive the keyboard notification only for one particular text view, so I want to tell addObserver that, but I don't know what to give it for the object. I tried the outlet variable that's bound to the text view I'm interested in, but that just resulted in my getting no notifications at all.

Any suggestions?

From stackoverflow
  • In this case you can not get the notification only for one particular text view, as the docu for UIKeyboardWillShowNotification says "The notification object is nil.".

    You have to check in your keyboardWillShow impl if your particular text view isFirstResponder.

  • The UIKeyboardWillShowNotification is coming from your window instance, and for all intents and purposes is a "system" notification. The keyboard is either showing or not showing, it's not really something that is tied to a specific control.

    If you want to do something when a user enters a specific text field, you should probably control that in the text field's delegate instead.

  • I had two competing needs - I had to use the keyboard notification because I needed to get the keyboard height, which appears to be only available that way, but I also needed to know which text view I was in, which meant also using the textViewDidBeginEditing delegate. After much messing around I finally resorted to getting the keyboard height in the notification method and storing it in an instance variable, which was then available to use in the delegate method (I need to scroll the view up so the bottom text view is not mostly hidden under they keyboard when they start typing). A bit inelegant, but it works.

    Thanks for the pointers!

Capture who clicked a link from Website A to Website B?

Assume I have a website called Website A and it has some links on it to other websites. One of these links could be a link to Website B. Is there a way, using some existing solution, open source, or something you have to pay for where you can track who, what, where, etc clicked the link to go from Website A to Website B?

Website B could be Stackoverflow and my website is www.websiteA.com, so on my website, I have a link to www.stackoverflow.com. If a user clicks on www.stackoverflow.com from my page, I want to capture information, such as name, phone, etc (not sure if I can do this unless the user is actually logged into my website). In this case, this is a public page which anyone from anywhere can access. If I can't do it from my website, is there a way I can tell stackoverflow that this particular person clicked on my link to get to your website and I need their information.

From stackoverflow
  • Try using HTTP_REFERER

    Chad Birch : Would only work if he also controls Website B, which he didn't specify.
    Xaisoft : I have no control over Website B, for example, imagine Website B is stackoverflow and I have a link to stackoverflow. If someone clicked a link on my site to get to stackoverflow, could I get their information.
    Gero : I see. So you can consider using the methods explained in other answers =)
  • You could always check the referer, but this can be spoofed.

    Request.UrlReferrer.ToString
    

    Other than that, you could append an additional query parameter on the links from Website A so you know where they came from:

    <a href="www.websiteb.com?from=websitea">
    
  • The method most sites use for this is to have a script on your site handle the redirect. So instead of the link being directly to:

    http://websiteB.com

    the link goes to something like:

    http://websiteA.com/link/websiteB.com

    The "link" script logs the request and then forwards them to the address (this should be done instantly, don't give them a "forwarding you to Website B in 3 seconds!" page).

    Note that as a visitor to your site, I much prefer a method where the link's destination is apparent when I hover over the link, as opposed to the method that some sites use where all you see is an id. What I mean is, instead of the link being something like:

    http://websiteA.com/link/websiteB.com

    they instead have a link like:

    http://websiteA.com/link/483

    Showing the destination can be achieved in a few ways. One way is something like I described above, where the destination is part of the query string. Alternatively, you can use some Javascript for this. The status bar's value can be changed with Javascript by setting the window.status variable, but many browsers prevent this by default (Firefox, for example).

    Google has found a sneaky way around this problem by actually using Javascript to change the link instead of the status bar. When the page is loaded, the links in the results list go to their actual destinations, but every link has an onmousedown javascript event that changes the link when you click on it. You can see this in action by right-clicking on a link. After you've done that, hovering over the link will now show the true destination (Google's click-tracking script) in the status bar instead of the result's real URL.

    Of course the information displayed with any method could be a misrepresentation of where I'm actually going to be sent, but I still prefer it over seeing a meaningless id number.

    Andrew Hare : +1 For a very nice explanation.
    Xaisoft : I am not to up-to-date on javascript, do you have an example of how this is done.
    Chad Birch : I will edit some information into my answer about it.
    Frank Crook : That's incredibly neat and something I've never noticed about the links Google provided. I had to play with the links by clicking down and not releasing before I believed you.
    Xaisoft : Is the Click-Tracking script open source to view?
  • If you control website B, then you just use the http_referer portion in logging. Otherwise, you will want to have a redirect page on site A, that records the url, then redirects the browser through to the other site/page.

    http://siteA/redirect?url=siteB/page
    

    the redirect page in site a redirects to..

    "http://" + request['url']
    

MySQL performance optimization: order by datetime field

I have a table with roughly 100.000 blog postings, linked to a table with 50 feeds via an 1:n relationship. When I query both tables with a select statement, ordered by a datetime field of the postings table, MySQL always uses filesort, resulting in very slow query times (>1 second). Here's the schema of the postings table (simplified):

+---------------------+--------------+------+-----+---------+----------------+
| Field               | Type         | Null | Key | Default | Extra          |
+---------------------+--------------+------+-----+---------+----------------+
| id                  | int(11)      | NO   | PRI | NULL    | auto_increment |
| feed_id             | int(11)      | NO   | MUL | NULL    |                |
| crawl_date          | datetime     | NO   |     | NULL    |                |
| is_active           | tinyint(1)   | NO   | MUL | 0       |                |
| link                | varchar(255) | NO   | MUL | NULL    |                |
| author              | varchar(255) | NO   |     | NULL    |                |
| title               | varchar(255) | NO   |     | NULL    |                |
| excerpt             | text         | NO   |     | NULL    |                |
| long_excerpt        | text         | NO   |     | NULL    |                |
| user_offtopic_count | int(11)      | NO   | MUL | 0       |                |
+---------------------+--------------+------+-----+---------+----------------+

And here's the feed table:

+-------------+--------------+------+-----+---------+----------------+
| Field       | Type         | Null | Key | Default | Extra          |
+-------------+--------------+------+-----+---------+----------------+
| id          | int(11)      | NO   | PRI | NULL    | auto_increment |
| type        | int(11)      | NO   | MUL | 0       |                |
| title       | varchar(255) | NO   |     | NULL    |                |
| website     | varchar(255) | NO   |     | NULL    |                |
| url         | varchar(255) | NO   |     | NULL    |                |
+-------------+--------------+------+-----+---------+----------------+

And here's the query that takes >1 second to execute. Please note that the post_date field has an index, but MySQL isn't using it to sort the postings table:

SELECT 
    `postings`.`id`, 
    UNIX_TIMESTAMP(postings.post_date) as post_date, 
    `postings`.`link`, 
    `postings`.`title`, 
    `postings`.`author`, 
    `postings`.`excerpt`, 
    `postings`.`long_excerpt`, 
    `feeds`.`title` AS feed_title, 
    `feeds`.`website` AS feed_website
FROM 
    (`postings`)
JOIN 
    `feeds` 
ON 
    `feeds`.`id` = `postings`.`feed_id`
WHERE 
    `feeds`.`type` = 1 AND 
    `postings`.`user_offtopic_count` < 10 AND 
    `postings`.`is_active` = 1
ORDER BY 
    `postings`.`post_date` desc
LIMIT 
    15

The result of the explain extended command on this query shows that MySQL is using filesort:

+----+-------------+----------+--------+---------------------------------------+-----------+---------+--------------------------+-------+-----------------------------+
| id | select_type | table    | type   | possible_keys                         | key       | key_len | ref                      | rows  | Extra                       |
+----+-------------+----------+--------+---------------------------------------+-----------+---------+--------------------------+-------+-----------------------------+
|  1 | SIMPLE      | postings | ref    | feed_id,is_active,user_offtopic_count | is_active | 1       | const                    | 30996 | Using where; Using filesort |
|  1 | SIMPLE      | feeds    | eq_ref | PRIMARY,type                          | PRIMARY   | 4       | feedian.postings.feed_id |     1 | Using where                 |
+----+-------------+----------+--------+---------------------------------------+-----------+---------+--------------------------+-------+-----------------------------+

When I remove the order by part, MySQL stops using filesort. Please let me know if you have any ideas on how to optimize this query to get MySQL to sort and select the data by using indexes. I have already tried a few things such as creating a combined index on all where/order by fields, as suggested by a few blog postings, but this didn't work either.

From stackoverflow
  • MySQL has two filesort algorithms: an older filesort that sorts records on disk, and a new version that works in memory.

    If it cannot use an index on the first table in the join to sort the query, it will have to do a filesort. If resultset before sorting converted to fixed-width format is greater than the sort buffer OR if it contains any text fields, it will have to use the slower on-disk filesort algorithm (the second condition is satisfied since your query has a text field).

    MySQL is choosing to use the is_active column, ostensibly because it thinks that column is most selective in eliminating rows before it continues with the other joins and where conditions. The first thing I would suggest would be to try creating composite indexes with post_date, feed_id, and the columns in the where condition, e.g. (is_active, user_offtopic_count, post_date, feed_id).

    Dennis G. : Thanks for the explanation!
  • Create a composite index either on postings (is_active, post_date) (in that order).

    It will be used both for filtering on is_active and ordering by post_date.

    MySQL should show REF access method over this index in EXPLAIN EXTENDED.

    Note that you have a RANGE filtering condition over user_offtopic_count, that's why you cannot use an index over this field both in filtering and in sorting by other field.

    Depending on how selective is your user_offtopic_count (i. e. how many rows satisfy user_offtopic_count < 10), it may be more useful to create an index on user_offtopic_count and let the post_dates be sorted.

    To do this, create a composite index on postings (is_active, user_offtopic_count) and make sure the RANGE access method over this index is used.

    Which index will be faster depends on your data distribuion. Create both indexes, FORCE them and see which is faster:

    CREATE INDEX ix_active_offtopic ON postings (is_active, user_offtopic_count);
    CREATE INDEX ix_active_date ON postings (is_active, post_date);
    
    SELECT 
        `postings`.`id`, 
        UNIX_TIMESTAMP(postings.post_date) as post_date, 
        `postings`.`link`, 
        `postings`.`title`, 
        `postings`.`author`, 
        `postings`.`excerpt`, 
        `postings`.`long_excerpt`, 
        `feeds`.`title` AS feed_title, 
        `feeds`.`website` AS feed_website
    FROM 
        `postings` FORCE INDEX (ix_active_offtopic)
    JOIN 
        `feeds` 
    ON 
        `feeds`.`id` = `postings`.`feed_id`
    WHERE 
        `feeds`.`type` = 1 AND 
        `postings`.`user_offtopic_count` < 10 AND 
        `postings`.`is_active` = 1
    ORDER BY 
        `postings`.`post_date` desc
    LIMIT 
        15
    
    /* This should show RANGE access with few rows and keep the FILESORT */
    
    SELECT 
        `postings`.`id`, 
        UNIX_TIMESTAMP(postings.post_date) as post_date, 
        `postings`.`link`, 
        `postings`.`title`, 
        `postings`.`author`, 
        `postings`.`excerpt`, 
        `postings`.`long_excerpt`, 
        `feeds`.`title` AS feed_title, 
        `feeds`.`website` AS feed_website
    FROM 
        `postings` FORCE INDEX (ix_active_date)
    JOIN 
        `feeds` 
    ON 
        `feeds`.`id` = `postings`.`feed_id`
    WHERE 
        `feeds`.`type` = 1 AND 
        `postings`.`user_offtopic_count` < 10 AND 
        `postings`.`is_active` = 1
    ORDER BY 
        `postings`.`post_date` desc
    LIMIT 
        15
    
    /* This should show REF access with lots of rows and no FILESORT */
    
    Dennis G. : That did the trick for me, thanks a lot! I had to use force index to get the best index used. We are now using multiple combined indexes for the different queries.
  • Also, it's important to remember that MySQL won't use an index if the column you're ordering by has a function applied to it.

    You should also try aliasing postings.post_date as something else. This will tell MySQL to order by the unaltered column, and you'll still select the unix timestamp.

Is there a way to add an onclick event to an ASP.NET Label server control?

I wanted to do something like this:

<asp:Label ID="lblMyLabel" onclick="lblMyLabel_Click" runat="server">My Label</asp:Label>

I know that in Javascript I can do:

<span onclick="foo();">My Label</span>

So I'm wondering why I can't do that with a Label object.

From stackoverflow
  • You can use Attributes to add onclick client side callback.

    I didn't know you can do this on span tags, but if it works you can add 'onclick' by lblMyLabel.Attributes.Add("onclick", "foo();");

    But foo(); would need to be a client side javascript function.

    System.Web.UI.WebControls.Label does NOT have OnClick server event. You could look into using AJAX if you want server callback with example above.

    You could also use LinkButton like other say. You can make it not look like a link by using CSS, if it is just that underline you are concerned about.

    ASPX:

    <asp:LinkButton ID="LinkButton1" runat="server" 
        CssClass="imjusttext" OnClick="LinkButton1_Click">
    LinkButton
    </asp:LinkButton>
    

    CSS:

    a.imjusttext{ color: #000000; text-decoration: none; }
    a.imjusttext:hover { text-decoration: none; }
    
  • You can do it in code in the page_load eg:

    void Page_Load(object sender, EventArgs e) 
    {
          lblMyLabel.Attributes.Add("onclick",
               "javascript:alert('ALERT ALERT!!!')");
    }
    
  • I think you can, but it's a client-side onclick handler, not server side. It will complain about the attribute not being supported (or some such) but I think it renders correctly. If you want to to a server-side handler, I think you'll need to do a LinkButton.

  • As far as I know that's impossible. Label control emits <span> element which is “unclickable” on the server side. You would need to replace your Label control with a LinkButton.

  • If you want an onclick event, why don't you use a linkbutton, which has the onclientclick event:

    <asp:linkbutton id="lblMyLink" onclientclick="lblMyLink_Click" runat="server">My Label</asp:linkbutton>
    

    You can use CSS to make the link look like whatever you want

    fuentesjr : I didn't want it to look like a link and I didn't want to hack a link to look like plain text using CSS, but I guess that's what I have to do. :( Thanks though.
  • Your question doesn't specify if you mean to raise the click event on the server (VB or c#) or the client (javascript.) If you're looking for a server-side event you should use a link button with css that makes the link appear as a label. You can then use the link button's server-side click event. If you're looking for a client-side click event - just type it into your server control markup (<asp:label id="MyLabel" runat="server" onclick="javascript:alert('hello');" Text="Click Me" />) ASP.NET will emit additional attributes in the html markup that generates.

  • Another hack would be to use a hidden link or button with style="display:none;" and trigger the click on the server control from a javascript function in the span.

    Something like this:

    <asp:linkbutton id="lblMyLink" onClick="lblMyLink_Click" runat="server" style="display:none;">My Link</asp:linkbutton>
    <span onclick="document.getElementById('lblMyLink').click();">My Label</span>
    
  • you could always roll out your own control which produces a span, with the .net's standard postback javascript, but as stated earlier using a linklabel with a CSS class would be easier

  • Just to chime in on this issue, I used a label in my .aspx page that was only to be visible in my DataList Item template if there were child records in the dataset.

    I added a onclick function to the label:

    moreOptionsLabel.Attributes.Add("onclick", string.Format("toggle_visibility('{0}')", div.ClientID));

    in my .cs file. It will now control a div tag in the .aspx page to show or hide the records - because the onclick points to a client side javascript function. Notice the div.ClientID, that makes this workable in a datalist.

    As noted above - the span tag does indeed become functional with "onclick". And since the label control is rendered as a span after the page request, using the Addtribute.Add("onclick".... does work.

    The result is show/hide functionality of data with out doing a postback. If you use the LinkButton, or simlar controls - the postback and page reload is unavoidable - unless you want to get into some Ajax stuff.

    NOTE: the span tag won't look clickable unless you style it with an underline and hand cursor.

    Credit to this idea comes from Will Asrari over at http://www.willasrari.com/blog/display-nested-repeaters-and-gridviews-asynchronously/000292.aspx

  • You need to create a class object that inherits from the label and onclick event handler which will be a method by yourslef and use your object as a custom label.

  • Thanks KiwiBastard, you helped me.

Find the last value in a "rolled-over" sequence with a stored procedure?

Suppose I had a set of alpha-character identifiers of a set length, e.g. always five letters, and they are assigned in such a way that they are always incremented sequentially (GGGGZ --> GGGHA, etc.). Now, if I get to ZZZZZ, since the length is fixed, I must "roll over" to AAAAA. I might have a contiguous block from ZZZAA through AAAAM. I want to write a sproc that will give me the "next" identifier, in this case AAAAN.

If I didn't have this "rolling over" issue, of course, I'd just ORDER BY DESC and grab the top result. But I'm at a bit of a loss now -- and it doesn't help at all that SQL is not my strongest language.

If I have to I can move this to my C# calling code, but a sproc would be a better fit.

ETA: I would like to avoid changing the schema (new column or new table); I'd rather just be able to "figure it out". I might even prefer to do it brute force (e.g. start at the lowest value and increment until I find a "hole"), even though that could get expensive. If you have an answer that does not modify the schema, it'd be a better solution for my needs.

From stackoverflow
  • You'd have to store the last allocated identifier in the sequence.

    For example, store it in another table that has one column & one row.

    CREATE TABLE CurrentMaxId (
        Id CHAR(6) NOT NULL
    );
    
    INSERT INTO CurrentMaxId (Id) VALUES ('AAAAAA');
    

    Each time you allocate a new identifier, you'd fetch the value in that tiny table, increment it, and store that value in your main table as well as updating the value in CurrentMaxId.

    The usual caveats apply with respect to concurrency, table-locking, etc.

  • I think I'd have tried to store the sequence as an integer, then translate it to string. Or else store a parallel integer column that is incremented at the same time as the alpha value. Either way, you could sort on the integer column.

  • A problem here is that you can't really tell from the data where the "last" entry is unless there is more detail as to how the old entries are deleted.

    If I understand correctly, you are wrapping around at the end of the sequence, which means you must be deleting some of your old data to make space. However if the data isn't deleted in a perfectly uniform manner, you'll end up with fragments, like below:

    ABCD   HIJKL NOPQRS   WXYZ
    

    You'll notice that there is no obvious next value...D could be the last value created, but it might also be L or S.

    At best you could look for the first or last missing element (use a stored procedure to perform a x+1 check just like you would to find a missing element in an integer sequence), but it's not going to provide any special result for rolled-over lists.

    Coderer : The sequence doesn't always start at the beginning. Sometimes, it starts at BBBB, while sometimes it starts at YYYY, so there's already "room" at the beginning of the alphabet
  • Since I don't feel like writing code to increment letters, I'd create a table of all valid IDs (AAAAAA through ZZZZZZ) with an integer from 1 to X for those IDs. Then you can use the following:

    SELECT @max_id = MAX(id) FROM Possible_Silly_IDs
    
    SELECT
        COALESCE(MAX(PSI2.silly_id), 'AAAAAA')
    FROM
        My_Table T1
    INNER JOIN Possible_Silly_IDs PSI1 ON
        PSI1.silly_id = T1.silly_id
    INNER JOIN Possible_Silly_IDs PSI2 ON
        PSI2.id = CASE WHEN PSI1.id = @max_id THEN 1 ELSE PSI1.id + 1 END
    LEFT OUTER JOIN My_Table T2 ON
        T2.silly_id = PSI2.silly_id
    WHERE
        T2.silly_id IS NULL
    

    The COALESCE is there in case the table is empty. To be truly robust you should calculate the 'AAAAAA' (SELECT @min_silly_id = silly_id WHERE id = 1) in case your "numbering" algorithm changes.

    If you really wanted to do things right, you'd redo the database design as has been suggested.

  • Here's code that I think will give you your Next value. I created 3 functions. The table is just my simulation of the table.column with your alpha ids (I used MyTable.AlphaID). I assume that it's as you implied and there is one contiguous block of five-character uppercase alphabetic strings (AlphaID):

    IF OBJECT_ID('dbo.MyTable','U') IS NOT NULL
        DROP TABLE dbo.MyTable
    GO
    CREATE TABLE dbo.MyTable (AlphaID char(5) PRIMARY KEY)
    GO
    -- Play with different population scenarios for testing
    INSERT dbo.MyTable VALUES ('ZZZZY')
    INSERT dbo.MyTable VALUES ('ZZZZZ')
    INSERT dbo.MyTable VALUES ('AAAAA')
    INSERT dbo.MyTable VALUES ('AAAAB')
    GO
    IF OBJECT_ID('dbo.ConvertAlphaIDToInt','FN') IS NOT NULL
        DROP FUNCTION dbo.ConvertAlphaIDToInt
    GO
    CREATE FUNCTION dbo.ConvertAlphaIDToInt (@AlphaID char(5))
    RETURNS int
    AS
    BEGIN
    RETURN 1+ ASCII(SUBSTRING(@AlphaID,5,1))-65
                  + ((ASCII(SUBSTRING(@AlphaID,4,1))-65) * 26)
                  + ((ASCII(SUBSTRING(@AlphaID,3,1))-65) * POWER(26,2))
                  + ((ASCII(SUBSTRING(@AlphaID,2,1))-65) * POWER(26,3))
                  + ((ASCII(SUBSTRING(@AlphaID,1,1))-65) * POWER(26,4))
    END
    GO 
    
    IF OBJECT_ID('dbo.ConvertIntToAlphaID','FN') IS NOT NULL
        DROP FUNCTION dbo.ConvertIntToAlphaID
    GO
    CREATE FUNCTION dbo.ConvertIntToAlphaID (@ID int)
    RETURNS char(5)
    AS
    BEGIN
    RETURN CHAR((@ID-1) / POWER(26,4) + 65)
          + CHAR ((@ID-1) % POWER(26,4) / POWER(26,3) + 65)
          + CHAR ((@ID-1) % POWER(26,3) / POWER(26,2) + 65)
          + CHAR ((@ID-1) % POWER(26,2) / 26 + 65)
          + CHAR ((@ID-1) % 26 + 65)
    
    END
    GO 
    IF OBJECT_ID('dbo.GetNextAlphaID','FN') IS NOT NULL
        DROP FUNCTION dbo.GetNextAlphaID
    GO
    CREATE FUNCTION dbo.GetNextAlphaID ()
    RETURNS char(5)
    AS
    BEGIN
        DECLARE @MaxID char(5), @ReturnVal char(5)
        SELECT @MaxID = MAX(AlphaID) FROM dbo.MyTable
        IF @MaxID < 'ZZZZZ'
            RETURN dbo.ConvertIntToAlphaID(dbo.ConvertAlphaIDToInt(@MaxID)+1)
        IF @MaxID IS NULL
            RETURN 'AAAAA'
        SELECT @MaxID = MAX(AlphaID) 
        FROM dbo.MyTable 
        WHERE AlphaID < dbo.ConvertIntToAlphaID((SELECT COUNT(*) FROM dbo.MyTable))
        IF @MaxID IS NULL
            RETURN 'AAAAA'
        RETURN dbo.ConvertIntToAlphaID(dbo.ConvertAlphaIDToInt(@MaxID)+1)
    END
    GO
    
    SELECT * FROM dbo.MyTable ORDER BY dbo.ConvertAlphaIDToInt(AlphaID)
    GO
    SELECT  dbo.GetNextAlphaID () AS 'NextAlphaID'
    

    By the way, if you don't want to assume contiguity, you can do as you suggested and (if there's a 'ZZZZZ' row) use the first gap in the sequence. Replace the last function with this:

    IF OBJECT_ID('dbo.GetNextAlphaID_2','FN') IS NOT NULL
        DROP FUNCTION dbo.GetNextAlphaID_2
    GO
    CREATE FUNCTION dbo.GetNextAlphaID_2 ()
    RETURNS char(5)
    AS
    BEGIN
        DECLARE @MaxID char(5), @ReturnVal char(5)
        SELECT @MaxID = MAX(AlphaID) FROM dbo.MyTable
        IF @MaxID < 'ZZZZZ'
            RETURN dbo.ConvertIntToAlphaID(dbo.ConvertAlphaIDToInt(@MaxID)+1)
        IF @MaxID IS NULL
            RETURN 'AAAAA'
        SELECT TOP 1 @MaxID=M1.AlphaID
        FROM dbo.Mytable M1
        WHERE NOT EXISTS (SELECT 1 FROM dbo.MyTable M2 
                          WHERE AlphaID = dbo.ConvertIntToAlphaID(dbo.ConvertAlphaIDToInt(M1.AlphaID) + 1 )
                         )
        ORDER BY M1.AlphaID
        IF @MaxID IS NULL
            RETURN 'AAAAA'
        RETURN dbo.ConvertIntToAlphaID(dbo.ConvertAlphaIDToInt(@MaxID)+1)
    END
    GO
    
  • To return the next ID for a given ID (with rollover), use:

    SELECT  COALESCE
            (
            (
            SELECT  TOP 1 id
            FROM    mytable
            WHERE   id > @id
            ORDER BY
                    id
            ),
            (
            SELECT  TOP 1 id
            FROM    mytable
            ORDER BY
                    id
            )
            ) AS nextid
    

    This query searches for the ID next to the given. If there is no such ID, it returns the first ID.

    Here are the results:

    WITH mytable AS
            (
            SELECT  'AAA' AS id
            UNION ALL
            SELECT  'BBB' AS id
            UNION ALL
            SELECT  'CCC' AS id
            UNION ALL
            SELECT  'DDD' AS id
            UNION ALL
            SELECT  'EEE' AS id
            )
    SELECT  mo.id,
            COALESCE
            (
            (
            SELECT  TOP 1 id
            FROM    mytable mi
            WHERE   mi.id > mo.id
            ORDER BY
                    id
            ),
            (
            SELECT  TOP 1 id
            FROM    mytable mi
            ORDER BY
                    id
            )
            ) AS nextid
    FROM    mytable mo
    
    id      nextid
    -----   ------
    AAA     BBB
    BBB     CCC
    CCC     DDD
    DDD     EEE
    EEE     AAA
    

    , i. e. it returns BBB for AAA, CCC for BBB, etc., and, finally, AAA for EEE which is last in the table.

    Coderer : Could you edit to explain what this actually does?
  • I think the lowest-impact solution for my needs is to add an identity column. The one thing I can guarantee is that the ordering will be such that entries that should "come first" will be added first -- I'll never add one with identifier BBBB, then go back and add BBBA later. If I didn't have that constraint, obviously it wouldn't work, but as it stands, I can just order by the identity column and get the sort I want.

    I'll keep thinking about the other suggestions -- maybe if they "click" in my head, they'll look like a better option.

Looking for pointers on MIDI driver development

I am looking for resources, documentation and general advices on writing a virtual MIDI device (see my previous question for reasons) and basics of generating MIDI events from a VST plugin.

The target platform is Windows, but Mac compatibility would be a plus.

From stackoverflow
  • I hope you don't mind me editing the subject of your question -- but I think that you shouldn't be so worried about the VST part of your software here, and the nature of your question is more about driver development than VST development.

    That said, you are essentially trying to write a normal MIDI driver, but you don't actually need to connect to any hardware. So writing a virtual driver is really not as hard as you anticipate... you just need to find the driver API's (see the edit to my answer on your previous question).

    One more thing I should add here, is that the best way to accomplish what you are trying to use a separate application with Rewire. So that is to say, you write an application which initializes a MIDI device when started, and connects to the sequencer via Rewire. Your users would start up the sequencer first, then your application, then send the audio on a bus track to your app, and configure their sequencer to receive MIDI from the device driver which you would write. The app, conversely, streams audio from rewire, does some type of FFT to get the pitches (or whatever you want to do to the audio stream), and then pushes those events out to the host through the MIDI driver API. IMO, that's probably the best way to solve this problem here, if I understand your project correctly.

    Luper Rouch : Yes, you understood what I want to do perfectly, but I'm looking for documentation material now.
  • Couldn't you just use existing virtual Midi devices such as Midi Yoke?

    Emile Vrijdags : Does midi yoke work on 64bit windows?

SQL Server 2005 - Trigger Loop?

I am using triggers for the first time.

If I update a field in a table by an update trigger on the same table, with this spark a loop? Does sql server guard against this recursive behavior?

Thanks

From stackoverflow
  • This page (search for RECURSIVE_TRIGGERS) describes some of the database settings you can use to modify this behavior. Also, one way to safeguard your procedures is to use either the UPDATE() function or the COLUMNS_UPDATED() function.

    If, for example, you have a table with columns A, B, and C, and you want the value of C to change automagically when the value in column B is updated, you can protect the call in the trigger:

    CREATE TRIGGER Whatever ON TableName AFTER UPDATE
    AS
    BEGIN
        IF UPDATE(B)
        BEGIN
            /* Update column C here */
        END
    END
    

    This way you avoid calling the trigger recursively when column C is updated by your trigger. COLUMNS_UPDATED() is also useful, but I find it to be fragile (relies on position of column instead of column name).

    Sung Meister : +1 great link and an example in the answer.
  • You can control recursion of triggers at the DB level via the RECURSION_TRIGGER option; it's turned off by default. Even if this option is turned on, there is a limit of 32 nested levels of triggers; all changes will be rolled back if your exit condition didn't stop the recursion before reaching the limit of 32 levels.