Storing Users Location Questionnaire Site - sql-server

Im building a website that has questionnaire that users fill out. Currently my db looks something like below.
Website Questionnaire Consists of
25 Questions
4 to 6 Answers per questions the user can choose from.
Issue
I want to add in the users country, state / province / city.
I need to incorporate this into my search function. See sql statement below.
The client provided me a list of 23 countries to store and 750 states/provinces and about 6000 cities.
Were should this go in my db? Im completely lost on this one?
Current DB Design
See fiddle http://sqlfiddle.com/#!6/bf068/1
User_Table
ID | UserName
0 | Jack
...
User Questionnaire_Questions_Answer
ID | user_id | question_id | answer_id
0 | 0 | 0 | 0
1 | 0 | 1 | 3
...
Questionnaire_Questions
ID | Question
0 | What type of music do you like?
1 | What is your favorite sport ?
...
Questionnaire_Answers
ID | Answer
0 | Rock
1 | Rap
2 | Basketball
3 | Soccer
...
SQL STATEMENT FOR SEARCH
Searches best questionnaire results based on what the preferences the user is looking for, Sorts on Count on highest totalmatches
SELECT
User_Table.id,
User_Table.UserName,
COUNT(User_Table.id) as totalMatches
FROM User_Table
INNER JOIN Questionnaire_Questions_Answer ON User_Table.id = Questionnaire_Questions_Answer.user_ID
INNER JOIN Questionnaire_Questions ON Questionnaire_Questions.id = Questionnaire_Questions_Answer.question_ID
INNER JOIN Questionnaire_Answers on Questionnaire_Answers.id = Questionnaire_Questions_Answer.answer_ID
WHERE
--Q and A Requested to Match
Questionnaire_Questions.id = '0' and Questionnaire_Answers.answer = '0'
OR
Questionnaire_Questions.id = '1' and Questionnaire_Answers.answer = '3'
GROUP BY User_Table.id
ORDER BY totalMatches DESC
Example Results
ID | Name | totalMatches
0 | Jack | 2

create a location table as so:
create table dbo.location (id int identity,
country varchar(200),
state_province varchar(100),
city varchar 100))
when you add a user add the id of the location to the user table.

Related

SQL Server: join one table with two table have same names but select all data from table first if available, otherwise from second table

I tried searching this kind of use case on google but did found exact what i was looking so, Need help to reach to specific point by useful answer, docs or any reference
I have this situation: table A has to be joined with table B and table C; B and C have similar columns, so there will be duplicate column names in select part, however give preferences to all the data of tables B if available otherwise show data from table c
For example:
SELECT
ae.*, ml.name as name, ml.contact AS contact,
ve.name AS name, ve.contact AS contact
FROM
TABLE ae
LEFT JOIN
TABLE ml ON ae.eid = ml.eid
LEFT JOIN
TABLE ve ON ae.eid = ve.eid
WHERE
ae.eid = 1
ml data
eid | name | contact
----+------+--------
1 | xyz | null
ve data
eid | name | contact
----+------+--------
1 | xyz | 1
ae data
eid | gender
----+--------
1 | male
I want this result:
eid | gender | name | contact
----+--------+------+--------
1 | male | xyz | null
But I am getting this for now:
eid | gender | name | contact | contact
----+--------+------+---------+--------
1 | male | xyz | 1 | null
I'm using node-mssql driver for querying SQL Server data.
Thanks,
You must join ve only if there is no matching row in ml and you do it if you add in the ON clause the condition ... AND ml.eid IS NULL.
Also use COALESCE() to select the columns from ml first and if they don't exist from ve:
SELECT ae.*,
COALESCE(ml.name, ve.name) AS name,
COALESCE(ml.contact, ve.contact) AS contact
FROM ae
LEFT JOIN ml ON ae.eid = ml.eid
LEFT JOIN ve ON ae.eid = ve.eid AND ml.eid IS NULL
WHERE ae.eid = 1
See the demo.
Results:
eid
gender
name
contact
1
male
xyz
null

SQL Server : optimize query. Lots of data

At the beginning I apologize for not being word-perfect in English.
I have two tables in my database, one contains questions, and second contains user answers for questions (for statistics).
TableA - questions
___________
| ID | Name |
TableB - Statistics
___________________________________
| ID | A_ID | U_ID| IsCorrect | Date|
User can answer one question several times, for example if we have question with ID = 1 and user (with id 2) which answered this question 4 times, we will add 4 rows to TableB:
___________________________________
| ID | A_ID | U_ID| IsCorrect | Date|
-------------------------------------
| 1 | 1 | 2 | True | Date|
| 2 | 1 | 2 | False | Date|
| 3 | 1 | 2 | False | Date|
| 4 | 1 | 2 | True | Date|
At the end, I have to query for questions (TableA) which user has not responded or answered but the smallest number of times (user is able to answer all questions).
My query (procedure) looks like:
Declare #max int
SET #max = (SELECT TOP 1 Count(A_ID) as QuestionCount FROM [TableB]
Where User_id = 1
GROUP BY A_ID
ORDER BY QuestionCount DESC)
SELECT TOP 40 ID
FROM [dbo].[TableA]
WHERE ID NOT IN (SELECT A_ID
FROM [dbo].[TableB]
WHERE User_id = 1
GROUP BY A_ID
HAVING Count(A_ID) = #max)
ORDER BY NewID()
At the beggining I query for max occurence of question - If user answered some question 4 time #max will be 4.
In second query I query for question which weren't answered yet (in this occurence).
Question is: How to optimize this query (or maybe I should change my tables)? TableB for now has almost one million rows and beacause of that it isn't fast enough.
With SQL-Server (>=2008) you can use the OVER clause (https://msdn.microsoft.com/en-us/library/ms189461.aspx) which gives you grouped aggregats.
EDIT: Just found your ORDER BY NewID() Why do you do this? NewID() is very bad to sort... 1 million is not so much in fact, but 1 million GUIDs without an index are a mass...

How to use field value of joined table for join rather than foreign key

I have a new database schema that I need to query and I am having trouble getting the data I need without using a primary key in the JOIN. I didn't design the schema but I have to work with it. I tried creating a SQLFiddle for this but it kept giving me a gateway error when I tried.
Basically I have a Contacts table that contains ALL the contacts stored in the database. The Contacts table contains two fields that reference the tables that the contact is linked to (Vendor and Client). The Contact table also contains a field named "ContactType". This links to another table that defines the type of Contact (Owner or Employee).
[Client]
ClientID | Name
==============================
1 | Uptown Dining
2 | Downtown Eatery
3 | Midtown Steakhouse
[Vendor]
VendorID | Name
==============================
1 | ABC Produce
2 | DEF Seafood
3 | GHI Meats
[ContactType]
ContactTypeID | Name
==============================
1 | Owner
2 | Employee
[Contact]
ContactID | TableName | TableID | ContactTypeID | FirstName | LastName
========================================================================
1 | Client | 1 | 1 | Bob | Smith
2 | Vendor | 1 | 1 | Jill | Weston
3 | Vendor | 1 | 2 | Alice | Jenkins
4 | Client | 2 | 1 | Chris | Brown
5 | Vendor | 3 | 2 | Todd | Davis
What I am trying to do is get a list of Client contacts who are the owners of their company. This SQl works to do just that.
SELECT
Contact.FirstName,
Contact.LastName
FROM Client
LEFT JOIN Contact
ON Client.ClientID = Contact.TableID
AND Contact.TableName = 'Client'
AND Contact.ContactTypeID = 1
This would give me ..
Bob Smith
Chris Brown
The problem is I don't want to use AND Contact.ContactTypeID = 1 in the JOIN. I want to be able to use Owner or Employee in place of the primary key (1 or 2) of the ContactType table in the JOIN but I am unsure of how to do this. Please keep in mind the restriction needs to be in the JOIN since I need to reserve the WHERE clause to filter specific clients.
For example what if I wanted to query for a specific Vendor and get a list of the owner and employee in the same row ...
Vendor Name | Owner First | Owner Last | Employee First | Employee Last
============================================================================
ABC Produce | Jill | Weston | Alice | Jenkins
I'm not sure I understand your problem correctly, but if I'm reading it right - you want to have the chance to specify "Owner" or "Employee" explicitly in your WHERE clause rather than 1 or 2.
Perhaps this is what you are looking for:
SELECT
Contact.FirstName,
Contact.LastName
FROM Client
LEFT JOIN Contact
ON Client.ClientID = Contact.TableID
AND Contact.TableName = 'Client'
LEFT JOIN ContactType ct ON ct.ContactTypeID = Contact.ContactTypeID
-- Now you can use it directly, as below
WHERE ct.Name = 'Owner'
Edit: Response to your comment
Not without getting messy... You can use a derived inline table, or a view, but all of these would be performance killers. The least performance-impairing (but still ugly) way I can think of is something like this:
SELECT
Contact.FirstName,
Contact.LastName
FROM Client
LEFT JOIN Contact
ON Client.ClientID = Contact.TableID
AND Contact.TableName = 'Client'
AND Contact.ContactTypeID = (SELECT iix.ContactTypeID FROM ContactType iix WHERE iix.Name = 'Owner')

Remove unnecessary joins in CakePHP 3 pagination

I have two tables:
Books
id
publisher_id
Publishers
id
name
Books is associated with Publishers in the BooksTable class like so:
public function initialize(array $config){
$this->belongsTo('Publishers');
}
I want to display a list of books along with their publishers, here is my code:
$query = $this->Books->find()
->contain('Publishers')
->select(['id', 'title', 'Publishers.name']);
$this->paginate($query);
This works fine, however I noticed that it produces the following queries:
SELECT * FROM books Books
LEFT JOIN publishers Publishers ON Publishers.id = (Books.publisher_id)
ORDER BY Books.id desc LIMIT 20 OFFSET 0
SELECT (COUNT(*)) AS `count` FROM books Books
LEFT JOIN publishers Publishers ON Publishers.id = (Books.publisher_id)
The Pagination component automatically strips out the unneeded code such as GROUP, ORDER, etc. from the second count query but it keeps the LEFT JOIN.
Is there a reason why this isn't removed and is there a way to tell it to ignore certain associations when querying the row count?
Say for example following is data for your table.
Books
id | publisher_id
-----------------
1 | 1
2 | 1
Publishers
id | name
----------
1 | publisher1
1 | publisher2
LEFT JOIN output
book_id | publisher_id | name
-----------------------
1 | 1 | publisher1
1 | 1 | publisher2
2 | 1 | publisher1
2 | 1 | publisher2
I know, For your case publisher_id is unique. but that's not case all time. Now you can see that LEFT JOIN has row count 4 while only 2 book there.

T-SQL replace value on select

I have a column (SERVICE_ID) in my table where I can have only 3 values: 0, 1 and 2.
On a SELECT, I'd like to change those values into English words for display:
select client, SERVICE_ID
from customers
Currently displays:
| John | 1
| Mike | 0
| Jordan | 1
| Oren | 2
I'd like to change the query to get:
| John | QA
| Mike | development
| Jordan | QA
| Oren | management
There is any way to do this using only the select?
SELECT client,
CASE SERVICE_ID
WHEN 0 THEN 'development'
WHEN 1 THEN 'QA'
WHEN 2 THEN 'management'
END AS SERVICE
FROM customers
Though I'd have a separate table for Services with columns SERVICE_ID, Name and join onto that to retrieve the service name.
CREATE TABLE `serviceID_lookup` (
`serviceID` INT(10) NULL,
`Description` VARCHAR(50) NULL
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB;
select c.client, sl.Description
from customers c
inner join serviceID_lookup sl on sl.serviceID = c.SERVICE_ID

Resources