Please start any new threads on our new
site at https://forums.sqlteam.com. We've got lots of great SQL Server
experts to answer whatever question you can come up with.
| Author |
Topic |
|
Jon Maz
Starting Member
9 Posts |
Posted - 2002-11-04 : 16:39:48
|
| Hi, I want to take the following T-SQL statement (which worked perfectly): IF EXISTS(SELECT 'True' FROM MyTable WHERE username = @username) and make MyTable into a variable. I know one way to do this - you build the SELECT statement as a string and then execute it.But that leads me to a problem; the following statement doesn't work: IF EXISTS EXEC('SELECT ''True'' FROM ' + @TableName + ' WHERE username = ' + @username) It gets this error message: Incorrect syntax near the keyword 'EXEC'. I'm starting to wonder if the syntax IF EXISTS EXEC(whatever...) is even possible. Any help appreciated! JON PS This is a continuation of a thread started at 11/4/2002 4:01:13 PM at the aspmessageboard Database Forum - http://www.aspmessageboard.com/forum/databases.asp?M=504818&T=504818&F=21&P=1. I couldn't get an answer there, and the suggestion (http://www.aspmessageboard.com/forum/databases.asp?M=504841&F=21&P=1) was that I might have better luck at sqlteam. |
|
|
Merkin
Funky Drop Bear Fearing SQL Dude!
4970 Posts |
Posted - 2002-11-04 : 18:29:09
|
HiNo, what you want to do isn't doable like that. One way you might look at is inserting into a temp table, then testing that for rows.Something like :SET NOCOUNT ONDeclare @sql nvarchar(2000)Declare @tablename nvarchar(200)Set @Tablename = 'authors'Create Table #temp( test char(4))Set @sql = 'Insert into #temp SELECT ''true'' FROM ' + @TableNameExec(@sql)IF EXISTS( Select * from #temp)print 'hi'drop table #tempSET NOCOUNT OFF Ugly, but it might help. Usually when you have a problem that needssomething nasty like this to solve it (especially when dealing withdynamic table names), it points to a problem in the design. Perhapswe can help out with that ?Hope that helps anywayDamianEdited by - merkin on 11/04/2002 18:30:10 |
 |
|
|
Jon Maz
Starting Member
9 Posts |
Posted - 2002-11-05 : 04:01:27
|
| Hi Damian,Thanks for the help! I'd be interested to know what you think of the comments over at aspmessageboard, where my whole strategy of creating dynamic SPs has been questioned. You too hint at "a problem in the design", so you might well agree!See http://www.aspmessageboard.com/forum/databases.asp?M=504868&F=21&P=1Cheers,JON |
 |
|
|
Merkin
Funky Drop Bear Fearing SQL Dude!
4970 Posts |
Posted - 2002-11-05 : 05:54:22
|
| HiI took a quick look at those posts. It seems from what I read that the tables are lookup tables with a very similar design. That sounds bad, you may be able to use one table with some sort of qualifier or type.As for code generation tools. The Lockwood tech stuff looks really good, and they are a great company to deal with. But, I would suggest... writing your own, it is a great learning exersise.Damian |
 |
|
|
Jon Maz
Starting Member
9 Posts |
Posted - 2002-11-05 : 06:05:18
|
| Hi,Correct, the tables in question are lookup tables with a very similar design, as in: tblCompanyTypes Fields: CompanyTypeID, CompanyType Info: 1, IT Info: 2, Waste Disposal tblTitles Fields: TitleID, Title, Deletable Info: 1, Mr, No Info: 2, Herr, Yes tblCountries Fields: CountryID, Country Info: 1, Afghanistan Info: 2, AngolaWould you recommend doing it a different way, with some kind of generic tblLookup like this?: tblLookup Fields: LookupID, LookupType, LookupInfo, Deletable (nulls allowed) Info: 1, CompanyType, IT, null Info: 2, Title, Mr, No Info: 3, Country, Afghanistan, nullJONPS Re:writing my own code generation tool, you're right, I should do it myself, but at my current rate of progress that would mean I won't get my current project done by the end of next year, never mind this! |
 |
|
|
Merkin
Funky Drop Bear Fearing SQL Dude!
4970 Posts |
Posted - 2002-11-05 : 06:11:35
|
I would just write separate procs. With some cut n paste it will only take you half an hour.Regarding tools..Say you have 10 hours to do a task, you can do it in 10 hours, or you can spend 8 hours writing a tool that will let you do it in 2. A good programmer will always write the tool. Because they can re-use that tool the next time they have a similar task.And it's fun writing developer tools is the most fun bit of my job. Maybe I should release some....Damian |
 |
|
|
Page47
Master Smack Fu Yak Hacker
2878 Posts |
Posted - 2002-11-05 : 07:39:03
|
OK, I'll bite ...First ...quote: Don't use SELECT 'TRUE' with an exists clause. If you use exists always use SELECT *-Dutch
...simply not true. select 'true', select 47, select * ... same execution plan, same cost and same performance.Next ...If you must do Dynamic SQL in conjunction with your an exists test, the two suggested methods (Merkins and Lars') work fine. I like Lars' better (sorry merk...) because it does not incur the overhead of the temp table creation.Next ...IMHO, this type of need for dynamic sql points to design problems. How many entitites in your design have a username property? And, if it is so hard to tell them apart that you can only make the decision at data retrieval time, they can't be all that different.Now, I understand you are wearing a couple different hats here. That happens a lot in practice. Keep this in mind. quote: "The DBA maps the logical model to a physical model, then makes the logical model accessible to application developers, who should not be exposed to the physical details."-Fabian Pascal
In this case, it is your physical model that is being exposed, rather than your logical model. If you look at your logical design and ask the question "Do I have any users with this username?" you should be able to point to one entity ... "Users" maybe ... and say to yourself look in here. It shouldn't matter how things are layed out on the physical layer (tables and such) ...If I were you, I would a.) take a look at your physical model and look at normalization. In which normal form are your tables? If you put together all the table that have a 'username' column (not as a FK, but as a direct dependency) which nf would you be? If there is sound relational design reasoning for your physical model, then you need to review how you are exposing your logical model. Maybe a view unioning all the usernames together is in order, then interface with the view.Jay White{0} |
 |
|
|
Merkin
Funky Drop Bear Fearing SQL Dude!
4970 Posts |
Posted - 2002-11-05 : 07:47:29
|
quote: I like Lars' better (sorry merk...) because it does not incur the overhead of the temp table creation.
I agree actually. When I posted that solution I didn't like it too much, but couldn't think of a better way off the top of my head (was designing a workflow system at the time....you know how it is )Damian |
 |
|
|
Jon Maz
Starting Member
9 Posts |
Posted - 2002-11-05 : 07:59:12
|
| To Damian: you are probably right, I'll have to reassess whether I should pause my immediate development work and write a tool for current and future use.To Jay: thanks for your wisdom! Your comments are pushing the envelope of my newbie-knowledge, so I couldn't tell you what normal form I'm using, I'm afraid, but I DID read a (relatively non-technical) book on database design, and it suggested doing lookup tables as I showed above (two postings ago, dated 11/05/2002 06:05:18). There IS a lot of duplication of structure between the tables, is this in fact a physical design error? tblCompanyTypes Fields: CompanyTypeID, CompanyType Info: 1, IT Info: 2, Waste Disposal tblTitles Fields: TitleID, Title, Deletable Info: 1, Mr, No Info: 2, Herr, Yes tblCountries Fields: CountryID, Country Info: 1, Afghanistan Info: 2, Angola The help is much appreciated!Cheers,JON |
 |
|
|
Onamuji
Aged Yak Warrior
504 Posts |
Posted - 2002-11-05 : 08:10:18
|
| Here's how I did one lookup table ...CREATE TABLE Properties( PropertyID INT NOT NULL IDENTITY(1,1) PRIMARY KEY NONCLUSTERED, ParentID INT NULL REFERENCES Properties(PropertyID), Description NVARCHAR(1024) NULL, Value NVARCHAR(1024) NULL, UNIQUE CLUSTERED (PropertyID, ParentID))Then say I needed to store a list of countries ... I would write a UDF (SQL 2000) to get them from the lookup tableCREATE FUNCTION Countries() RETURNS TABLE AS RETURN (SELECT PropertyID, Description, Value FROM Properties WHERE ParentID = 1) -- Assuming ParentID with a value of 1 is the root for all country lookup valuesThen I would need a way to make sure foreign keys only reference the proper lookup id...CREATE FUNCTION IsCountry(@PropertyID INT) RETURNS BIT AS BEGIN RETURN CAST(ISNULL((SELECT 1 FROM Properties WHERE ParentID = 1 AND PropertyID = @PropertyID), 0) AS BIT) ENDthen my table would beCREATE TABLE MyTable( Something INT, CountryID INT NOT NULL REFERENCES Properties(PropertyID), CONSTRAINT MyTable_CountryID_CK CHECK(dbo.IsCountry(CountryID) = 1))... assuming the lookup table won't get too large you could pin it in the server settings so that access is very fast (if you have enough RAM that is)... my average lookup table ranges from 100 to 10,000 records ... I store constants and any application settings that can be stored there (so the application admin can control the system completely)... |
 |
|
|
Page47
Master Smack Fu Yak Hacker
2878 Posts |
Posted - 2002-11-05 : 08:19:47
|
| Understanding of normalization is critical to working with databases. Do youself a favor and learn that stuff. You'll probably answer your question for yourself.Which book was it that suggested the lookup scheme above? You should personally find the author and hit him over the head with his book. Just look at the problems it giving you already ... not to mention the fact that it takes a way you ability to use sql server to enforce referential integrity ... I blabbed about this more here.So I take it you don't actually have more than one table with a username column. Rather you are using dynamic sql because you are getting the table from this tblLookup. You really need to revisit your design and learn normalization ...By the way, read Reason number 5 of why Joe Celko hates you.Jay White{0} |
 |
|
|
Page47
Master Smack Fu Yak Hacker
2878 Posts |
Posted - 2002-11-05 : 08:29:56
|
Geez, Onamuji ... what's wrong with ...CREATE TABLE MyTable ( Something INT, Country varchar(30) NOT NULL REFERENCES World(Country) ) CREATE TABLE World ( Country varchar(30) not null primary key ) Tell me, "Yeah, I tried that and my way and found that a 7% improvement with the lookup table and function because of the nature of my queries". mkay, fine ... otherwise, why make things more complicated (and I bet slower for DML considering the check constraint)?I believe 9 times out of 10, the natural key is best for a lookup table. For most queriesl you will never have to join to the lookup table because the value is right there in your MyTable. Why inject a non-relational, non-standard, non-protable, contrived, made up, confusing, hard to read, surrogate key into your physical design if you don't have too ...Jay White{0}Edited by - Page47 on 11/05/2002 08:31:12 |
 |
|
|
Jon Maz
Starting Member
9 Posts |
Posted - 2002-11-05 : 08:44:56
|
| Hi,Thanks Onamuji for your posting.To Jay: the book I read is (wait for it...) "Database Design for Mere Mortals", but I think it's my ignorance you should blame, not any deficiencies in the book.I'm afraid maybe my postings haven't been as clear as they should. The terrible truth is that actually NONE of my lookup tables have a Username column - I simplified my initial example to avoid confusion (!!). I think I'll abandon that particular strategy, and show you the true horrors (or not) of my DB. The lookup tables I have in the DB are these ones: tblCompanyTypes Fields: CompanyTypeID, CompanyType Info: 1, IT Info: 2, Waste Disposal tblTitles Fields: TitleID, Title, Deletable Info: 1, Mr, No Info: 2, Herr, Yes tblCountries Fields: CountryID, Country Info: 1, Afghanistan Info: 2, Angola and I have an sp_Add for each one of them (code below), ie 3 lookup tables = 3 sp_Adds, and there are many more lookup tables to come. As you'll see, the SPs are identical, so I thought "why not scrap all these individual SPs (sp_AddTitle, sp_AddCompanyType, sp_AddCountry etc) and make one dynamic sp_AddLookupItem, to which I pass a TableName, an IDColumnName, a ColumnName, and the new LookupItem to be added". My aim was to save time having to cut and paste practically identical SPs into the DB. It was when I ran into problems creating this generic, dynamic SP that I first posted to aspmessageboard.Hope that makes things clearer - am I still a normalisation-dunce?JONHere are the individual SPs I wanted to replace:CREATE PROCEDURE sp_AddTitle @NewTitle varchar(50), @TitleID int OUTPUTAS IF EXISTS (SELECT 'True' FROM tblTitles WHERE Title = @NewTitle) BEGIN --This means it exists, return -1 as the identity of the "new" record SET @TitleID = -1 END ELSE BEGIN --This means the record isn't in there already, let's go ahead and add it INSERT INTO tblTitles(Title) VALUES (@NewTitle) SET @TitleID = @@IDENTITY ENDGO +++++++++++++++++++CREATE PROCEDURE sp_AddCompanyType @NewCompanyType varchar(50), @CompanyTypeID int OUTPUTAS IF EXISTS (SELECT 'True' FROM tblCompanyTypes WHERE CompanyType = @NewCompanyType) BEGIN --This means it exists, return -1 as the identity of the "new" record SET @CompanyTypeID = -1 END ELSE BEGIN --This means the record isn't in there already, let's go ahead and add it INSERT INTO tblCompanyTypes(CompanyType) VALUES (@NewCompanyType) SET @CompanyTypeID = @@IDENTITY ENDGO +++++++++++++++++++CREATE PROCEDURE sp_AddCountry @NewCountry varchar(50), @CountryID int OUTPUTAS IF EXISTS (SELECT 'True' FROM tblCountries WHERE Country = @NewCountry) BEGIN --This means it exists, return -1 as the identity of the "new" record SET @CountryID = -1 END ELSE BEGIN --This means the record isn't in there already, let's go ahead and add it INSERT INTO tblCountries(Country) VALUES (@NewCountry) SET @CountryID = @@IDENTITY ENDGO |
 |
|
|
Page47
Master Smack Fu Yak Hacker
2878 Posts |
Posted - 2002-11-05 : 09:27:58
|
quote: am I still a normalisation-dunce?
If you are not sure, then you probably are ..OK, I think I am understanding your problem a bit more ... fine. In fact, your desire to get rid of these seemingly almost redundant stored procedures is somewhat commendable.Why no add a param to proc for value type?create procedure usp_addvalue @newvalue varchar(50), @valuetype char(1), @id int output asset nocount oninsert into tbltitles(title)select @newvaluewhere not exists ( select 1 from tbltitles where title = @newvalue) and @valuetype = 't'select @id = case when @@rowcount = 0 then -1 else @@identity endinsert into tblCompanytypes(companyType)select @newvaluewhere not exists ( select 1 from tblCompanytypes where companytype = @newvalue) and @valuetype = 'c'select @id = case when (@@rowcount = 0 and @id is null) then -1 else @@identity end-- ect, ect ...go Jay White{0} |
 |
|
|
Jon Maz
Starting Member
9 Posts |
Posted - 2002-11-05 : 13:37:10
|
| Hi,Now we're getting somewhere! I guess explaining clearly what I wanted has helped...This is what I'm currently doing in the Data Layer: Function AddLookup (TableName) Select Case TableName Case Title 'Call sp_AddTitle Case CompanyType 'Call sp_AddCompanyType Case Country 'Call sp_AddCountry End Select End FunctionThis works, it's just that for every new lookup table XXX in the DB I have to create another sp_AddXXX (not to mention sp_DeleteXXX etc), and add another "Case XXX" branch to the AddLookup function. My aim was to try to automatise this, so that I only need to set one parameter (a table name) in the presentation layer control that displays info from the lookup table, and the code automatically adjusts everything else.I thought I could do this by amending the AddLookup function to pass a TableName (the parameter taken from the presentation layer control), IDColumnName and ColumnName (both of which are derived automatically from the TableName) as well as a NewValue (also from the presentation layer control) to my ONE dynamic sp_AddLookup. Thus when I add a new lookup to the DB, for example a table of Towns, I wouldn't have to add another SP (or even another branch to an existing SP, as your solution would require), nor another branch to the AddLookup Function, I would just pass the appropriate TableName, IDColumnName and ColumnName, and it would all work like a charm.Here's half a solution I adapted from suggestions at aspmessageboard (http://www.aspmessageboard.com/forum/databases.asp?M=504860&T=504818&F=21&P=1): CREATE PROCEDURE sp_AddLookup @TableName varchar(255), @ColumnName varchar(255), @NewLookup varchar(50), @NewLookupID int OUTPUT AS SET NOCOUNT ON DECLARE @intExists INT, @strSQL VARCHAR(4000) SET @strSQL = 'SELECT @exists = count(*) FROM ' + @TableName + ' WHERE ' + @ColumnName + ' = ''' + @NewLookup + '''' EXEC sp_executesql @strSQL, '@exists INT OUTPUT', @intExists OUTPUT IF @intExists > 0 SET @NewLookupID = -1 ELSE -- need to add code to do an INSERT here GO I got stalled here because I couldn't figure out how to do the INSERT bit (I have never used sp_executesql before, I just copied that bit, and am a bit out of my depth). Do you have any ideas how you could modify this SP or your version to do what I want?Thanks for all the help!Cheers,JONEdited by - Jon Maz on 11/06/2002 03:03:30 |
 |
|
|
Merkin
Funky Drop Bear Fearing SQL Dude!
4970 Posts |
Posted - 2002-11-06 : 06:45:06
|
quote: Now we're getting somewhere! I guess explaining clearly what I wanted has helped...
Yes, it usually does  quote: so that I only need to set one parameter (a table name) in the presentation layer control that displays info from the lookup table, and the code automatically adjusts everything else.
Sounds like a good plan. I would write separate stored procs for your data access, then use the function in your business or presentation layer to pass the table or object name dynamically.Damian |
 |
|
|
Jon Maz
Starting Member
9 Posts |
Posted - 2002-11-06 : 07:07:24
|
Hi,OK, glad you like the plan. I have no problem passing the table name and column name from the presentation layer, through the business layer to the data layer, the problem comes when the data layer passes these parameters to the dynamic SP.Two postings ago (11/05/2002 13:37:10) I sent a half-finished dynamic SP - any idea how I'd finish it off? It's beyond my meagre SQL / SQL Server skills, I'm afraid. Here it is again: CREATE PROCEDURE sp_AddLookup @TableName varchar(255), @ColumnName varchar(255), @NewLookup varchar(50), @NewLookupID int OUTPUT AS SET NOCOUNT ON DECLARE @intExists INT, @strSQL VARCHAR(4000) SET @strSQL = 'SELECT @exists = count(*) FROM ' + @TableName + ' WHERE ' + @ColumnName + ' = ''' + @NewLookup + '''' EXEC sp_executesql @strSQL, '@exists INT OUTPUT', @intExists OUTPUT IF @intExists > 0 SET @NewLookupID = -1 ELSE -- need to add code to do an INSERT here GO I think the first half of it is OK, but there's a missing part where I have put the comment "need to add code to do an INSERThere". You can see the static SPs I'm trying to replace a furthertwo postings back (11/05/2002 08:44:56), but I'll repeat them here: CREATE PROCEDURE sp_AddTitle @NewTitle varchar(50), @TitleID int OUTPUT AS IF EXISTS (SELECT 'True' FROM tblTitles WHERE Title = @NewTitle) BEGIN --This means it exists, return -1 as the identity of the "new" record SET @TitleID = -1 END ELSE BEGIN --This means the record isn't in there already, let's go ahead and add it INSERT INTO tblTitles(Title) VALUES (@NewTitle) SET @TitleID = @@IDENTITY END GO +++++++++++++++++++ CREATE PROCEDURE sp_AddCompanyType @NewCompanyType varchar(50), @CompanyTypeID int OUTPUT AS IF EXISTS (SELECT 'True' FROM tblCompanyTypes WHERE CompanyType = @NewCompanyType) BEGIN --This means it exists, return -1 as the identity of the "new" record SET @CompanyTypeID = -1 END ELSE BEGIN --This means the record isn't in there already, let's go ahead and add it INSERT INTO tblCompanyTypes(CompanyType) VALUES (@NewCompanyType) SET @CompanyTypeID = @@IDENTITY END GO +++++++++++++++++++ CREATE PROCEDURE sp_AddCountry @NewCountry varchar(50), @CountryID int OUTPUT AS IF EXISTS (SELECT 'True' FROM tblCountries WHERE Country = @NewCountry) BEGIN --This means it exists, return -1 as the identity of the "new" record SET @CountryID = -1 END ELSE BEGIN --This means the record isn't in there already, let's go ahead and add it INSERT INTO tblCountries(Country) VALUES (@NewCountry) SET @CountryID = @@IDENTITY END GO Cheers,JONEdited by - Jon Maz on 11/06/2002 07:09:23 |
 |
|
|
Page47
Master Smack Fu Yak Hacker
2878 Posts |
Posted - 2002-11-06 : 07:28:34
|
quote: I have no problem passing the table name and column name from the presentation layer, through the business layer to the data layer, the problem comes when the data layer passes these parameters to the dynamic SP.
Did I studder? Was I unclear? Or am I being told to talk-to-the-hand?I highly recommend you NOT expose your physical design to your application. I highly recommend you NOT pass a tablename as a param from your front end.Didn't I post a working start to a generic proc? Doesn't it not use dynamic SQL? Doesn't it take a lookup value type as a param and insert into the correct lookup table? Is this thing on (as he taps the mic and get high-pitched feedback)?Jay White{0} |
 |
|
|
Jon Maz
Starting Member
9 Posts |
Posted - 2002-11-06 : 07:41:13
|
Hi Jay,quote: Didn't I post a working start to a generic proc? Doesn't it not use dynamic SQL? Doesn't it take a lookup value type as a param and insert into the correct lookup table? Is this thing on (as he taps the mic and get high-pitched feedback)?
Yes to all of the above! But it's not clear to me (no doubt it will be after your next posting, bad newbie, stupid newbie...) WHY it's a bad idea to pass a table name as a parameter from the front end. After all, it seems to save some repetitive coding in the SP, and that seems to me to be a good thing. After all, it was the quest to save repetitive coding that started me down this line of thinking in the first place.JONPS I suspect the answer lies in your previous quote: quote: "The DBA maps the logical model to a physical model, then makes the logical model accessible to application developers, who should not be exposed to the physical details." -Fabian Pascal
Like I say, it's not clear to me why the application shouldn't talk to the physical layer, as long as this is done through a business and data layer, and not directly between presentation layer and datastore. |
 |
|
|
Jon Maz
Starting Member
9 Posts |
Posted - 2002-11-06 : 07:47:35
|
Actually, I may have just figured it out for myself. If I am setting a TableName parameter in the presentation layer, then my business and data layers are just shams, as any change in the datastore would have to be reflected in a parallel change in the presentation layer, where the parameter's life begins. So I haven't really separated out the layers at all.Is that it? I AM new to 3-tier design after all... JON |
 |
|
|
Page47
Master Smack Fu Yak Hacker
2878 Posts |
Posted - 2002-11-06 : 08:05:03
|
| That hits the nail on the head (for this particular situation) ...In general, it is best practice to expose the logical design of the data tier to the applications and leave the physical implementation to the dbas ... But don't trust me, go out and read ...Jay White{0} |
 |
|
|
Next Page
|
|
|
|
|