Fog Creek Software
g
Discussion Board




SQL question

I have a table that stores answers to survey questions.  Each survey has maybe 10 questions, with answers being "Totally Disagree, Somewhat Disagree, Somewhat Agree, Totally Agree".

I'd like to build a report that assigns values to each answer, on a scale of 1 - 4.  I created a lookup table, with the four possible answers and their respective values.  Now I'm stuck while trying to build the query.  I would have thought I could just do a Join, but each row has essentially 10 lookups.  Am I missing something?

Thanks.

nathan
Tuesday, December 9, 2003

Provide the table schema (create table statements).

MR
Tuesday, December 9, 2003

Original table:
CREATE TABLE [tableName] (
    [tp_ID] [int] IDENTITY (1, 1) NOT NULL ,
    [tp_Title] [nvarchar] (255) NULL ,
    [tp_Modified] [datetime] NULL ,
    [tp_Created] [datetime] NULL ,
    [tp_Author] [int] NULL ,
    [tp_Editor] [int] NULL ,
    [tp_Version] [int] NULL ,
    [tp_Name] [nvarchar] (255)  NULL ,
    [tp_DOB] [datetime] NULL ,
    [RecordNumber] [float] NULL ,
    [tp_Survey_x0020_Date] [datetime] NULL ,
    [Question1] [nvarchar] (255) NULL ,
    [Question2] [nvarchar] (255) NULL ,
    [Question3] [nvarchar] (255) NULL ,
    [Question4] [nvarchar] (255) NULL ,
    [Question5] [nvarchar] (255) NULL ,
    [Question6] [nvarchar] (255) NULL ,
    [Question7] [nvarchar] (255) NULL ,
    [Question8] [nvarchar] (255) NULL ,
    [Question9] [nvarchar] (255) NULL ,
    [tp_Comments_x003a_] [ntext] NULL ,
    [tp_Entity] [int] NULL ,
    [tp_Program] [int] NULL
) ON [PRIMARY]

Lookup table:
CREATE TABLE [ResponseLookup] (
    [response] [nvarchar] (255) NOT NULL ,
    [score] [int] NOT NULL
) ON [PRIMARY]

nathan
Tuesday, December 9, 2003

All the Question columns need to be replaced with the value in the lookup table.

nathan
Tuesday, December 9, 2003

1 Test can have many different questions.
1 Question can have many different answers.
1 Answer can be used for many different Questions.

Create 4 tables

tblTest
tblQuestions
tblQAIntersect
tblAnswers

Make sure to have a primary key in each and a foreign key in the ones that need to be joined.

tblQA Intersect would have 2 fields as the primary key, the tblQuestions PK and the tblAnswers PK.

This may help make the query easier to write.  Unless I don't understand the problem fully.

Dave B.
Tuesday, December 9, 2003

That's not a very good table design -- this is a classic problem that arises when you don't bother with any normalization.

You should have something like (I don't care what the table/column names are):
Survey( SurveyID, date, user, etc. )
Question( QuestionID, Text, Weight, etc. )
Question_Answer( SurveyID, QuestionID, Answer )

Then you can easily join to get the answers and the weight.

With your current system you'd have to do 10 joins, or ignore the lookup table and hard-code the weight in a CASE statement.  Good luck.

MR
Tuesday, December 9, 2003

>With your current system you'd have to do 10 joins, or ignore the lookup table and hard-code the weight in a CASE statement.  Good luck.

That's what I'm stuck with.  I didn't create the original table, and I can't change it ever.  It's created by an [expletive deleted] application we use.  I did create the lookup table as a first attempt to get the data in some kind of usable format.

Is there another lookup table format that would be more helpful?

nathan
Tuesday, December 9, 2003

Well like I said, you can do 10 joins:
select testID,
          t1.weight,
          t2.weight,
          ...,
          t10.weight
  from test
inner join weight t1 on test.question1 = t1.question
inner join weight t2 on ...
....

Or maybe you could do something like:
select testID,
  case question1
    when 'Foo'
      then 1
    when 'bar'
      then 2
  else
      1213
  end,
  case question2
    etc.


There might be some other SQL tricks that you can do, but I don't think that you can do it easily.

MR
Tuesday, December 9, 2003

You may still be able to create your own tables and "Select" the data from the original into your own or possible write a VB program to do it.

Dave B.
Tuesday, December 9, 2003

>With your current system you'd have to do 10 joins,

Ok.  Here's what I came up with.  If anyone knows a better way without changing changing existing tables (adding tables is fine), please let me know.

SELECT [tp_Survey_x0020_Date],
    R.score as [Question1],
    E.score as [Question2],
    S.score as [Question3],
    P.score as [Question4],
    O.score as [Question5],
    N.score as [Question6],
    U.score as [Question7],
    L.score as [Question8],
    K.score as [Question9]
FROM SurveyTable
JOIN ResponseLookup R ON [Question1] = R.response
JOIN ResponseLookup E ON [Question2] = E.response
JOIN ResponseLookup S ON [Question3] = S.response
JOIN ResponseLookup P ON [Question4] = P.response
JOIN ResponseLookup O ON [Question5] = O.response
JOIN ResponseLookup N ON [Question6] = N.response
JOIN ResponseLookup U ON [Question7] = U.response
JOIN ResponseLookup L ON [Question8] = L.response
JOIN ResponseLookup K ON [Question9] = K.response

nathan
Tuesday, December 9, 2003

Maybe you could design some views that make the data a little more normalized, and then join the views together? It's clearly not going to be performant no matter what you do, but you may at least be able to simplify the select SQL.

Brad Wilson (dotnetguy.techieswithcats.com)
Tuesday, December 9, 2003

Thanks guys.  looks like the 10 joins is the quickest, though definitely one of the dirtiest.

nathan
Tuesday, December 9, 2003

You could add a table which simulates the normalized schema I had before:

e.g.
create table question_answer(
testID,
question,
answer
)

-- this is all T-SQL pseudocode
create trigger survey_update
on
survey
for update

if update( question1 )
  -- change the question_answer table
...
end

create trigger survey_insert
on survey
for insert
  insert into question_answer( inserted.quizID, 1, inserted.Question1 )
  insert into question_answer( inserted.QuizID, 2, inserted.Question2 )
...
end

create trigger survey_delete
on survey
for delete

  delete from question_answer where quiz_id = deleted.quizID

end

Then you could do the join to question_answer.

MR
Tuesday, December 9, 2003

Might I ask what the [expletive deleted] application is, so the rest of us may avoid such misfortune in the future?

Greg Hurlman (blogs.squaretwo.net)
Tuesday, December 9, 2003

CommandBridge from Mariner.  Apparently it's built on top of SharePoint Portal Services.  The IT dept made the decision to use this before I was hired.  I'd rather just use SPS rather than using this product that sits on top.  Overall the software is ok, just in this one "self-service" component things get messy.  They allow end-users to create surveys & forms on the fly.  Their method of storing the data in these ad hoc forms is through the table structure I showed earlier.

nathan
Tuesday, December 9, 2003

It seems like it is just using the normal SharePoint Team services list/survey functionality, so not only are you using a crappy application, but your company actually paid extra for it to make STS (which is included in Office XP for free) even worse than it already was? (or did they pay extra to make the full Sharepoint Portal Server worse?)

ChrisO
Wednesday, December 10, 2003

I think we paid more to get STS.  We also got MapPoint, and supposedly we can do some cool stuff with what we have, just nobody knows how to do any of it, let alone explain it to the end users.  It's a mess.

nathan
Wednesday, December 10, 2003

*  Recent Topics

*  Fog Creek Home