Help with Voting Table Schema Idea - sql

I'm trying to create a voting table and maximize performance. Since a vote can only be UP or DOWN, I'm thinking of using bit where 1 = up and 0 = down is this unintuitive? is there a better way?
UserVotes (3 way primary key between all three tables)
+----------+----------+-------------+
| UserID | IsUp | CommentID |
+----------+----------+-------------+
| 1 | 1 | 99 |
| 2 | 0 | 99 |
etc.
The updates will happen when a user clicks a vote up or a vote down button
If VoteUpButtonClicked Then
VoteService.Add(userID,True, CommentID)
End If
If VoteDownButtonClicked Then
VoteService.Add(userID, False, CommentID)
End If
Then the calls will be "count"
Dim TotalUpVotes = VoteServce.QueryVotes().Where(Function(v) v.IsUp And v.CommentID = CommentID).Count
Dim TotalDownVotes = VoteService.QueryVotes().Where(Function(v) Not v.IsUp And v.CommentID = CommentID).Count
I'm using SQL Server 2008 and Linq to SQL.
And yes, I would like to allow users to delete a vote.

How about:
UserId (int) PK
CommentId (int) PK
Vote (tinyint) (1 or -1)
Voted (Date)
Then you can simply sum the values

I would have the voting table keep a live sum of the votes:
UpVote int
DownVote int
+1 to the applicable column when a user vote.
Store the votes in a log table
UserID
CommentID
IsUp (bit)
If a user deletes his vote, you can interrogate IsUp and -1 on either UpVote or DownVote.

Since you are the one that will be wrapping the table in logic to accomplish the requirements of your project, I would assume it is you that needs to answer the question of whether or not it is intuitive. It needs to be intuitive to you.

Related

SQL - Tracking student exam records as they move between schools

I'd like to pick some of your glorious minds for an optimal solution to my dilemma.
Scenario:
Schools have children and children take tests.
The tests point to the child, not the school.
If the child moves school, the test records are taken to the new school and the previous school has no record of the test being done as they are linked to the child.
Obviously, this isn't ideal and is the result of the database not being designed with this in mind. What would the correct course of action be; I’ve currently identified the 3 possibilities listed below which would solve the current problem. However, i cannot be sure which is best for the issue at hand - and if any better solutions exist.
Have each test store the school & student within the test records (requiring current records to be updated & increasing the size of the database)
Create a new child record, duplicating the existing data for the new school with a new ID so the test remains linked to the previous school (complicating the ability to identify previous test scores)
Separately keep track of moves to other schools, then use this additional table to identify current and previous using the timestamps (increased complexity and computational requirements)
EDIT:
So i tried to use a basic example, but requests for the task at hand have been requested.
Here's the DB Schema for the tables (simplified for problem, note: Postnatal is not important):
Patients: ID, MidwifeID, TeamID
Midwives: ID
Groups: ID
GroupsMidwives: MidwifeID, GroupsID
PatientObservations: ID, MidwifeID, PatientID
Using a query as follows:
SELECT Some Information
from Postnatals
JOIN Midwives on Postnatals.MidwifeID = Midwives.ID
JOIN Patients on Patients.PatientID = Postnatals.PatientID
JOIN GroupsMidwives on GroupsMidwives.MidwifeID = Midwives.ID
JOIN Groups on Groups.ID = GroupsMidwives.GroupID
JOIN PatientObservations on PatientObservations.PatientID =
Postnatals.PatientID
WHERE groups.Name = ?
*some extra checks*
GROUP BY Midwives.Firstname, Midwives.Surname, Midwives.ID
However, in the event that a midwife is moved to a different team, the data associated with the previous team is now owned by the newly assigned team. As described in the example detailed previously.
Thus a modification (which modification is yet to be realised) is required to make the data submitted - prior to a team change - assigned to the previous team, as of current, because of the way the records are owned by the midwife, this is not possible.
You should below suggestion as per you concern.
Step 1 ) You need to create School Master Table
ID | School | IsActive
1 | ABC | 1
2 | XYZ | 1
Step 2 ) You need to create Children Master having school id as foreign key
ID | School | Children Name| IsActive
1 | 2 | Mak | 1
2 | 2 | Jak | 1
Step 3 ) You need to create test table having children id as foreign key
ID | Children_id | Test Name | IsActive
1 | 2 | Math | 1
2 | 2 | Eng | 1
Now whenever child moves school then make child record inactive and create another active record with new school. This will help you to bifurcate the old test and new test.
do let me know in case morehelp required

SQL composite key value vs string

I have a list of integer from 1 to N elements (N < 24)
At the moment, there are two solutions to manage this value in a SQL database (I think it is the same for MySQL and Microsoft SQL Server)
Solution 1: use VARCHAR and , to separate integer values:
aaa | 40,50,50,10,600,200
aab | 40,50,600,200
aac | 40,50,50,10,600,200,500,1
Solution 2: create a new table with composite primary key (key, id) (id = index of element in list) and value:
aaa | 0 | 40
aaa | 1 | 50
aaa | 2 | 50
....
aab | 0 | 40
aab | 1 | 50
aab | 2 | 600
....
What is it better solution considering I have many items of data to load and I need to refresh this data many times
Thanks
Edit:
my operative case is: i need to refresh/read all data (list for key) with same call and i never call one by one, this is why i think first approach better.
And all math like avg or max i wanna do on client.
Usually the second approach is preferable. One advantage is ease of access:
-- Third value of aaa
select value from mytable where key = 'aaa' and pos = 3;
-- Avarage value of aaa
select avg(value) from mytable where key = 'aaa';
-- Avarage number of values
select avg(cnt) from (select count(*) as cnt from mytable group by key) counted;
Another is data consistency. You can add simple constraints to your columns, such as to allow only integers from, say, 1 to 700 and positions only up to 23.
There is an exception to the above, though. If you use the database only to store the list as is and you don't want to select separate values or even aggregate them, i.e. if this is just a string to the DBMS and your queries don't care about its content, then store it as a simple string. Why not?
The second solution that you propose is the classic way of doing this, I would recommend that.
The first solution is quite terrible in scaling and in other hundred things

MySQL query (joining tables)

QuestionsTable
id* (int) | question_text (string) | question_type (int)
AlternativesTable
id* (int) | question_id (int) | alternative_text (string) | is_correct (bool)
AnswersTable
id* (int) | question_id (int) | alternative_id (int) | answer_text (string)
" * " = primary key
Every question can either be of type free text or multiple selector. A multiple selector question has one or more alternatives and only one can be correct.
An answer is defined by a question_id and an alternative_id (multiple selector) or an answer_text (free text). The is_correct bool is so I can mark which answer is the correct one.
How do I make an SQL query that will give me all the alternatives listed for every question with a count on each alternative that shows how many has selected it? Say I can store it in an array and iterate through it with an foreach-loop and show it as the example below.
An example, every question is represented by it's question_text, and every alternative beloning to that question is represented by Alt1 (alternative_text), Alt2, and so on... The numbers after the alternatives is the number of selections it got (answers).
MultiQuestion1
Alt1 | 3
Alt2 | 2
Alt3 | 0
MultiQuestion2
Alt1 | 2
Alt2 | 3
FreeText1
Answer1
Answer2
....
I can make the query that gives me all questions and all the alternatives that belong to it, but I fail when I try to get the count on all answers for every question alternative.
So now I could use some help from a SQL-ninja =)
Thanks in advance Daniel
Off the top of my head, this might work:
SELECT alternatives.id, COUNT(DISTINCT(answers.id))
FROM alternatives
LEFT JOIN answers ON alternatives.id = answers.altid
GROUP BY alternatives.id;
That should give you the total number of times each alternative occurs in the answers table.

MySQL change a value depending on its original value

Hy everyone,
Long time reader, first time poster.
This sounds like it should be really simple but I can't find the solution anywhere. I'm building a ratings system where people can rate if something is active or not. It has its own little logic but for it to work I need to.
check the items rating
depending on the current rating change it to a pre set amount.
I could hard code it in PHP with two SQL statements but I'm sure using a single stored procedure (one for vote up, another for vote down) will be much faster.
example table:
item_id | item_rating
---------------------
10 | 1
logic to vote item_rating up:
if | then
---------
0 | 1
1 | 2
-1 | 1
-2 | 1
2 | 2
logic to vote item_rating down:
if | then
---------
0 | -1
1 | -1
-1 | -2
-2 | -2
2 | -1
I know a simple points based system would be easier but due to the nature of the system this is the simplest solution I could find.
Can someone explain how I would use IF statements in SQL to achieve this? I'm sure the answer is really obvious to someone in the know.
(btw using the latest version of MySQL)
Is this what you're looking for? Here's an upvote:
UPDATE rating
SET item_rating = IF(item_rating < 1, 1, 2);
Here's a downvote:
UPDATE rating
SET item_rating = IF(item_rating > -1, -1, -2);
This is untested, but I think it should work.
update items i
set item_rating = (select i.item_rating + `then` from item_rating
where `if` = i.item_rating)
where i.item_id = 10

How should I go about implementing an "autonumber" field in SQL Server 2005?

I'm aware of IDENTITY fields but I have a feeling that I couldn't use one to solve my problem.
Let's say I have multiple clients. Each client has multiple orders. Each client needs to have their orders numbered sequentially, specific to them.
Example table structure:
Orders:
OrderID | ClientID | ClientOrderID | etc...
Some example rows for this table would be:
OrderID | ClientID | ClientOrderID | etc...
1 | 1 | 1 | ...
2 | 1 | 2 | ...
3 | 2 | 1 | ...
4 | 3 | 1 | ...
5 | 1 | 3 | ...
6 | 2 | 2 | ...
I know the naive way would be to take the MAX ClientOrderID for any client and use that value for INSERTs but that would be subject to concurrency issues. I was considering using a transaction but I'm not quite sure what the broadest isolation scope that can be used for this. I'll be using LINQ to SQL but I have feeling that isn't relevant.
Somebody correct me if I'm wrong, but as long as your MAX() call is in the same step as your insert, you won't have a problem with concurrency.
So, you could not do
select #newOrderID=max(ClientOrderID) + 1
from orders
where clientid=#myClientID;
insert into ( ClientID, ClientOrderID, ...)
values( #myClientID, #newOrderID, ...);
But you can do
insert into ( ClientID, ClientOrderID, ...)
select #myClientID, max(ClientOrderID) + 1, ...
from orders
where clientid=#myClientID;
I'm assuming OrderID is an identity column.
Again, if I'm incorrect on this, please let me know. Preferably with a URL
You could use a Repository pattern to handle your Orders and let it control the number of each specific clients order number. If you implement the OrderRepository correctly it could control the concurrency and number the order before saving it to the database (let the repository and not the db set the number).
Repository pattern: http://martinfowler.com/eaaCatalog/repository.html
One possibility (though I don't like to do this) is to have a lookup table that would tell you the greatest Order Number given for each vendor. Inside of a transaction, you'd fetch the most recent one from VendorOrderNumber, save your new order, increment the value in VendorOrderNumber, commit transaction.
This is an odd way to store data, but assuming you need it, there is nothing built-in that you can use.
Your suggestion of Max(ClientOrderID) is straight forward and pretty easy to implement (follow John MacIntyre's advice). It will probably work acceptably well on tables with a few thousand orders. As the table grows this approach will of course slow down.
Nick DeVore's suggestion of a lookup table is a little messier to implement but won't substantially be affected by data growth.
Depending on where/when you actually need the ClientOrderID, you could calculate the id when needed like this:
SELECT *,
ROW_NUMBER() OVER(ORDER BY OrderID) AS ClientOrderID
FROM Orders
WHERE ClientID = 1
This assumes that the ClientOrderIDs are in the same sequence as the OrderID. Without actually persisting the ID, it is awkward to use as a key to anything else. This approach should not be affected by data growth.