| Author |
Topic |
|
Kristen
Test
22859 Posts |
Posted - 2010-02-23 : 05:02:09
|
I want to select a single row from a table (using PK ID), but if does not exist I still want a row  SELECT @Action = CASE WHEN MyPK_ID IS NULL THEN 'Add' ELSE 'Update' END, @MyPK_ID = CASE WHEN MyPK_ID IS NULL THEN -1 ELSE MyPK_ID ENDFROM( SELECT 1) AS X LEFT OUTER JOIN MyTable ON MyPK_ID = @MyPK_ID maybe that is a daft way of doing it? I was trying to keep all the logic in the SELECT in one place. Alternative is to split the logic over two places:SELECT @Action = 'Update', @MyPK_ID = MyPK_IDFROM MyTableWHERE MyPK_ID = @MyPK_IDIF @@ROWCOUNT = 0BEGIN SELECT @Action = 'Add', @MyPK_ID = -1END but my thinking is the logic in one place is better for maintenance (less chance of missing updating both parts of the SELECT logic).But my first example looks really terrible!Any suggestions? |
|
|
madhivanan
Premature Yak Congratulator
22864 Posts |
Posted - 2010-02-23 : 06:24:42
|
Make use of this hint  select case when count(*)>=1 then 'update' else 'add' end from MyTablewhere MyPK_ID =@MyPK_ID MadhivananFailing to plan is Planning to fail |
 |
|
|
Kristen
Test
22859 Posts |
Posted - 2010-02-23 : 06:35:27
|
| I like the COUNT(*), thanks Madhi.I also need some columns from MyTable if it exists. Can I do that somehow?Perhaps using ROW_NUMBER() or OUTPUT (in some crafty way)? |
 |
|
|
madhivanan
Premature Yak Congratulator
22864 Posts |
Posted - 2010-02-23 : 07:43:59
|
quote: Originally posted by Kristen I like the COUNT(*), thanks Madhi.I also need some columns from MyTable if it exists. Can I do that somehow?Perhaps using ROW_NUMBER() or OUTPUT (in some crafty way)?
If you need other columns if there exists data for the input, I think it is better you do it in two-stepsIn Your example you used variables that get updated. How do you want other columns along with them?MadhivananFailing to plan is Planning to fail |
 |
|
|
Kristen
Test
22859 Posts |
Posted - 2010-02-23 : 09:38:21
|
| "How do you want other columns along with them?"In variables also.The sort of scenario is:On a web page allow the user to Vote and enter a comment. They are voting on whether they like a song. If they view that Song again their previous Vote / Comment is shown and can be changed. I need to prevent them voting 2+ times.VOTE table has PK and a unique alternative reference (User_ID + Song_ID).If user has not Voted yet then VOTE record will not exist.User may try to hack the voting system and not submit their Vote_ID from previous vote - so I need to also check User_ID + Song_ID.If I find a row (by either means) I need to return Vote_ID (NULL = New), User_ID and Song_ID - as variables.I always have User_ID. I may have Vote_ID or Song_ID, or both (need to check they correspond).I will use those to further test for vote rigging etc. |
 |
|
|
madhivanan
Premature Yak Congratulator
22864 Posts |
Posted - 2010-02-23 : 10:24:03
|
Will this help you?SELECT @Action = case when counting>=1 then 'Update' else 'Add', @MyPK_ID = case when counting>=1 then @MyPK_ID else -1,...(select count(*) as counting FROM MyTableWHERE MyPK_ID = @MyPK_ID) as t MadhivananFailing to plan is Planning to fail |
 |
|
|
Kristen
Test
22859 Posts |
Posted - 2010-02-23 : 10:56:41
|
Not quite, because I may not have been given @MyPK_ID in the Sproc parameters.I currently have something like this:SELECT @UpsertFlag = 1, -- Use UpSert to save @TrackID = NULL, -- Do NOT change existing value (during save) @VoteID = VoteID, @SongID = SongID, @ErrorNo = CASE WHEN ErrorNo = 0 THEN @ErrorNo ELSE ErrorNo END, @strErrMsg = @strErrMsg + CASE WHEN ErrorNo = 0 THEN '' ELSE 'Error in uspMySproc[' + CONVERT(varchar(20), ErrorNo) + '] ' ENDFROM( SELECT VoteID, [ErrorNo] = CASE WHEN (@SongID IS NOT NULL AND SongID <> @SongID) OR (@VoteID IS NOT NULL AND VoteID <> @VoteID) THEN 1 ELSE 0 END FROM dbo.VoteLog WHERE (SongID = @SongID OR VoteID = @VoteID) AND UserID = @UserID) AS XIF @@ROWCOUNT = 0BEGIN SELECT @UpsertFlag = 0, -- Insert new record @VoteID = NULL IF @SongID IS NULL BEGIN SELECT @ErrorNo = -3, @strErrMsg = @strErrMsg + 'SQL Error(3) @SongID=[' + CONVERT(varchar(20), @SongID) + ']. ' ENDEND plus some code to handle @@ROWCOUNT > 1I was looking if I could get it into one statement so the logic was all in one place ...Perhaps I could use MAX(@VoteID) and then LEFT JOIN that to the VoteLog table to have the row as well? |
 |
|
|
madhivanan
Premature Yak Congratulator
22864 Posts |
Posted - 2010-02-24 : 02:07:55
|
| [code]SELECT @UpsertFlag = case when counting>=1 then 1 else 0 end, -- Use UpSert to save @TrackID = NULL, -- Do NOT change existing value (during save) @VoteID = case when counting>=1 then VoteId else NULL end, @SongID = case when counting>=1 then SongID else NULL end, @ErrorNo = case when counting>=1 then CASE WHEN ErrorNo = 0 THEN @ErrorNo ELSE ErrorNo END else -3 end, @strErrMsg = case when counting>=1 then @strErrMsg + CASE WHEN ErrorNo = 0 THEN '' ELSE 'Error in uspMySproc[' + CONVERT(varchar(20), ErrorNo) + '] ' END else 'SQL Error(3) @SongID=[' + CONVERT(varchar(20), @SongID) + ']. ' endFROM( SELECT max(VoteID) as VoteId, [ErrorNo] = MAX(CASE WHEN (@SongID IS NOT NULL AND SongID <> @SongID) OR (@VoteID IS NOT NULL AND VoteID <> @VoteID) THEN 1 ELSE 0 END), count(*) as counting FROM dbo.VoteLog WHERE (SongID = @SongID OR VoteID = @VoteID) AND UserID = @UserID) AS X[/code]But I dont why do you want to do in a single code. I think it may get complicated when you add some more logic in the futureMadhivananFailing to plan is Planning to fail |
 |
|
|
Kristen
Test
22859 Posts |
Posted - 2010-02-24 : 02:38:30
|
I think you are right Madhi. I just think that if code is in two places there is more chance of it becoming "different" during maintenanceWhat do you think about this approach?SELECT @UpsertFlag = CASE WHEN VoteID IS NULL Counting=0 THEN 0 ELSE 1 END, -- 0=Insert, 1=UpSert @TrackID = CASE WHEN VoteID IS NULL Counting=0 THEN @TrackID ELSE NULL END, -- Do NOT change existing value (during save) @VoteID = CASE WHEN VoteID IS NULL Counting=0 THEN @VoteID ELSE VoteID END, @SongID = CASE WHEN VoteID IS NULL Counting=0 THEN @SongID ELSE SongID END, @ErrorNo = @ErrorNo + CASE WHEN VoteID IS NULL Counting=0 THEN '' ELSE CASE WHEN (@SongID IS NOT NULL AND SongID <> @SongID) OR (@VoteID IS NOT NULL AND VoteID <> @VoteID) THEN 1 ELSE 0 END END, @strErrMsg = ... CASE WHEN [counting] > 1 THEN 'Multiple records found' ELSE '' END ...FROM( SELECT [MAX_VoteID] = MAX(VoteID), [counting] = COUNT(*) FROM dbo.VoteLog WHERE (SongID = @SongID OR VoteID = @VoteID) AND UserID = @UserID) AS X LEFT OUTER JOIN dbo.VoteLog ON VoteID = MAX_VoteID |
 |
|
|
madhivanan
Premature Yak Congratulator
22864 Posts |
Posted - 2010-02-24 : 02:44:29
|
Is VoteID in the outer part actually MAX_VoteID?Yes. The approach seems correct. But TEST needs to test it MadhivananFailing to plan is Planning to fail |
 |
|
|
Kristen
Test
22859 Posts |
Posted - 2010-02-24 : 03:34:59
|
" Is VoteID in the outer part actually MAX_VoteID?"Its from the LEFT JOIN, so is NULL if there is no row (i.e. MAX_VoteID IS NULL) - but I do need to test that that works OK!!In fact I think I will use [counting] for that instead ...Edit: I made that change above. I think it is easier to read, thanks for that |
 |
|
|
madhivanan
Premature Yak Congratulator
22864 Posts |
Posted - 2010-02-24 : 04:02:57
|
<<Its from the LEFT JOIN, >>Ok. As you dont specifiy the table alias I thought it is typo <<In fact I think I will use [counting] for that instead >>Yes that makes more sense and readable Also note that [counting] > 1 should be [counting] >= 1MadhivananFailing to plan is Planning to fail |
 |
|
|
Kristen
Test
22859 Posts |
Posted - 2010-02-24 : 05:17:34
|
"Also note that [counting] > 1 should be [counting] >= 1"Thanks. Actually that's just looking for an error. Should be 0 or 1 rows only, if it gets more than 1 then someone has found a way to hack the system!! (i.e. the PK and a separate Unique Index have been dropped )Right, I'm sorted with this now I think. I like the solution, thanks for your help. |
 |
|
|
madhivanan
Premature Yak Congratulator
22864 Posts |
Posted - 2010-02-24 : 05:26:55
|
quote: Originally posted by Kristen "Also note that [counting] > 1 should be [counting] >= 1"Thanks. Actually that's just looking for an error. Should be 0 or 1 rows only, if it gets more than 1 then someone has found a way to hack the system!! (i.e. the PK and a separate Unique Index have been dropped )Right, I'm sorted with this now I think. I like the solution, thanks for your help.
You are welcome MadhivananFailing to plan is Planning to fail |
 |
|
|
|