Social Network Database Design - Friend/Block Relationships - sql-server

I'm working on a social networking site and need users to be able to friend each other and/or block each other. The way I see it, 2 users can either be Friend, Pending, Block, or NULL. I'd like to have a single view that shows a single row for each confirmed relationship. My view properly shows the relationship but I had to do a workaround to only show 1 row/relationship without unioning the table with itself and swapping the order or Requestor and Requestee.
Anybody have any ideas about how to clean this up?
Thanks,
- Greg
Relationship Table:
Requestor (int) | Requestee (int) | ApprovedTimestamp (smalldatetime) | IsBlock (bit)
vwRelationship View:
SELECT DISTINCT
CASE WHEN f.Requestor < f.Requestee THEN f.Requestor ELSE f.Requestee END AS UserA,
CASE WHEN f.Requestor < f.Requestee THEN f.Requestee ELSE f.Requestor END AS UserB, CASE WHEN b.Requestor IS NULL AND b.Requestee IS NULL
THEN CASE WHEN f.AcceptedTimestamp IS NULL THEN 'Pending' ELSE 'Friend' END ELSE 'Block' END AS Type
FROM dbo.Relationship AS f LEFT OUTER JOIN
(SELECT Requestor, Requestee
FROM dbo.Relationship
WHERE (IsBlock = 1)) AS b ON f.Requestor = b.Requestor AND f.Requestee = b.Requestee OR f.Requestor = b.Requestee AND f.Requestee = b.Requestor
Example Query:
Select Type From vwRelationship Where (UserA = 1 AND UserB = 2) OR (UserA = 2 AND UserB = 1)
Scenario:
User 1 and User 2 don't know each other | Relationship Type = NULL
User 1 friends User 2 | Relationship Type = Pending
User 2 accepts | Relationship Type = Friend
a month later User 2 blocks User 1 | Relationship Type = Block

Here's what I ended up using:
Table - Relationship
RelationshipID, RelationshipTypeID, CreatedByUserID, CreatedTimestamp
Table - RelationshipType
RelationshipTypeID, RelationshipTypeName
Table - UserRelationship
UserID, RelationshipID, IsPending
Anybody think of anything better?

Related

Database Design For reporting comparison results [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
We are going to design an table architecture. Here I wanted to compare same kind of data coming from different sources say Source_A and Source_B. I have to compare few attributes and identify below cases
Mismatches in attribute1
Mismatches in attribute2
Data that are missing in source_A
Data that are missing in Source_B.
Finally i have report to the same in PowerBI with charts. For now I have 2 tables A_DATA and B_DATA to store the incoming data and both are having below structure (this is just a sample, I have lot more columns)
+---------------+
| Columns |
+---------------+
| Material_ID |
+---------------+
| Material_Name |
+---------------+
| Material_Type |
+---------------+
| Quantity |
+---------------+
Now I'm confused whether should I create separate table for 4 cases(Mismatch,Source_A missing,Source_B Missing) or In single table I should have one more column saying Status and keep everything there. For reporting in PowerBI (like out of 1K rows, 5K are mismatches). Please suggest which one is better for reporting cases. Im really confused.
I would say neither of your two options (additional column, or new table) are optimal. I think this would be best handled with a view. Something like:
CREATE VIEW MisMatches
AS
SELECT Material_ID = ISNULL(a.Material_ID, b.Material_ID),
Status = CASE WHEN a.Material_ID IS NULL THEN 'Mising A'
WHEN b.Material_ID IS NULL THEN 'Mising B'
WHEN a.Material_Name <> b.Material_Name THEN 'Mismatch Name'
WHEN a.Material_Type <> b.Material_Type THEN 'Mismatch Type'
WHEN a.Quantity <> b.Quantity THEN 'Mismatch Quantity'
END,
MaterialName_A = a.Material_Name,
MaterialName_B = b.Material_Name,
Material_Type_A = a.Material_Type,
Material_Type_B = b.Material_Type,
Quantity_A = a.Quantity,
Quantity_B = b.Quantity
FROM A_Data AS a
FULL JOIN B_Data AS b
ON b.Material_ID = a.Material_ID
WHERE CHECKSUM(a.Material_Name, a.Material_Type, a.Quantity) <> CHECKSUM(b.Material_Name, b.Material_Type, b.Quantity);
This short circuits on your status column which may not be what you want, that is to say that if you name, quantity and type all don't match, then the status will only tell you that the name is a mismatch. If you want all mis-matches you will need to extend the case expression slightly. Also, if any of your columns are nullable, you will need to handle this in the Status case expression, e.g.
WHEN a.Quantity <> b.Quantity OR a.Quantity IS NULL OR b.Quantity IS NULL THEN ...
I have also had to make an assumption about how you identify a match, but hopefully this gives the general gist of it
Edit
There is a better way of doing this rather than CHECKSUM:
CREATE VIEW MisMatches
AS
SELECT Material_ID = ISNULL(a.Material_ID, b.Material_ID),
Status = CASE WHEN a.Material_ID IS NULL THEN 'Mising A'
WHEN b.Material_ID IS NULL THEN 'Mising B'
WHEN a.Material_Name <> b.Material_Name THEN 'Mismatch Name'
WHEN a.Material_Type <> b.Material_Type THEN 'Mismatch Type'
WHEN a.Quantity <> b.Quantity THEN 'Mismatch Quantity'
END,
MaterialName_A = a.Material_Name,
MaterialName_B = b.Material_Name,
Material_Type_A = a.Material_Type,
Material_Type_B = b.Material_Type,
Quantity_A = a.Quantity,
Quantity_B = b.Quantity
FROM A_Data AS a
FULL JOIN B_Data AS b
ON b.Material_ID = a.Material_ID
WHERE NOT EXISTS
( SELECT a.Material_Name, a.Material_Type, a.Quantity
INTERSECT
SELECT b.Material_Name, b.Material_Type, b.Quantity
);
I discovered this read the following article: Undocumented Query Plans: Equality Comparisons

Django sum boolean fields horizontally

I have a django model that records multiple user preferences horizontally.
class Preferences(models.Model):
user = models.ForeignKey(CustomUser,on_delete='CASCADE')
choice1 = models.BooleanField()
choice2 = models.BooleanField()
choice3 = models.BooleanField()
choice4 = models.BooleanField()
choice5 = models.BooleanField()
I'm trying to achieve the SQL query of:
(select
(case when choice1 = True then 1 else 0 end) +
(case when choice2 = True then 1 else 0 end) +
(case when choice3 = True then 1 else 0 end) +
(case when choice4 = True then 1 else 0 end) +
(case when choice5 = True then 1 else 0 end) + as choice_sum
from Preferences)
How should I go about doing this in Django?
In case you want to know, I'm storing them horizontally as each user will have to record preferences for all choices which would increase in the future and I don't want multiple rows of unnecessary user FK.
Edit:
I realised my question might seem a bit weird. My objective is to eventually run a query that selects records where there is at least one True for any of the choices fields.
My objective is to eventually run a query that selects records where
there is at least one True for any of the choices fields.
Using Django filter + Q objects
from django.db.models import Q
.....
.....
choices = Preferences.objects.filter(Q(choice1=True) | Q(choice2=True) | Q(choice3=True) | Q(choice4=True) | Q(choice5=True))

Adding multiple records from a string

I have a string of email addresses. For example, "a#a.com; b#a.com; c#a.com"
My database is:
record | flag1 | flag2 | emailaddresss
--------------------------------------------------------
1 | 0 | 0 | a#a.com
2 | 0 | 0 | b#a.com
3 | 0 | 0 | c#a.com
What I need to do is parse the string, and if the address is not in the database, add it.
Then, return a string of just the record numbers that correspond to the email addresses.
So, if the call is made with "A#a.com; c#a.com; d#a.com", the rountine would add "d#a.com", then return "1, 3,4" corresponding to the records that match the email addresses.
What I am doing now is calling the database once per email address to look it up and confirm it exists (adding if it doesn't exist), then looping thru them again to get the addresses 1 by 1 from my powershell app to collect the record numbers.
There has to be a way to just pass all of the addresses to SQL at the same time, right?
I have it working in powershell.. but slowly..
I'd love a response from SQL as shown above of just the record number for each email address in a single response. That is, "1,2,4" etc.
My powershell code is:
$EmailList2 = $EmailList.split(";")
# lets get the ID # for each eamil address.
foreach($x in $EmailList2)
{
$data = exec-query "select Record from emailaddresses where emailAddress = #email" -parameter #{email=$x.trim()} -conn $connection
if ($($data.Tables.record) -gt 0)
{
$ResponseNumbers = $ResponseNumbers + "$($data.Tables.record), "
}
}
$ResponseNumbers = $($ResponseNumbers+"XX").replace(", XX","")
return $ResponseNumbers
You'd have to do this in 2 steps. Firstly INSERT the new values and then use a SELECT to get the values back. This answer uses delimitedsplit8k (not delimitedsplit8k_LEAD) as you're still using SQL Server 2008. On the note of 2008 I strongly suggest looking at upgrade paths soon as you have about 6 weeks of support left.
You can use the function to split the values and then INSERT/SELECT appropriately:
DECLARE #Emails varchar(8000) = 'a#a.com;b#a.com;c#a.com';
WITH Emails AS(
SELECT DS.Item AS Email
FROM dbo.DelimitedSplit8K(#Emails,';') DS)
INSERT INTO YT (emailaddress) --I don't know what the other columns value should be, so have excluded
SELECT E.Email
FROM dbo.YourTable YT
LEFT JOIN Emails E ON YT.emailaddress = E.Email
WHERE E.Email IS NULL;
SELECT YT.record
FROM dbo.YourTable YT
JOIN dbo.DelimitedSplit8K(#Emails,';') DS ON DS.Item = YT.emailaddress;

SQL Subquery with a COUNT

I have a table of our communications containing; Created User, Created Date & Sub Code. I want the output to have 4 columns in SSRS, showing communications from the previous month;
Comms logged Dealt With % Dealt With
Created User 1
Created User 2
So far I've got;
SELECT
[EM-COMMUNICATION].[CRT-USER]
,COUNT([EM-COMMUNICATION].[CRT-USER]) AS LOGGED
,(SELECT COUNT([EM-COMMUNICATION].[CRT-USER]) FROM [EM-COMMUNICATION] WHERE [EM-COMMUNICATION].[SUB-CODE] = N'DEALTWITH' AND DateDiff(MONTH,[EM-COMMUNICATION].[CRT-DATE],GetDate()) = 1) AS Dealt
FROM
[EM-COMMUNICATION]
WHERE
DateDiff(MONTH,[EM-COMMUNICATION].[CRT-DATE],GetDate()) = 1
GROUP BY
[EM-COMMUNICATION].[CRT-USER]
The problem I'm having is that the sub query is returning a count of comms for all users, instead of matching the groupings of the main query, i.e., each row has the same count in 'Dealt With'
You need to relate your subquery with your main query using AND [EM-COMMUNICATION].[CRT-USER] = T.[CRT-USER]
SELECT
[EM-COMMUNICATION].[CRT-USER]
,COUNT([EM-COMMUNICATION].[CRT-USER]) AS LOGGED
,(SELECT COUNT([EM-COMMUNICATION].[CRT-USER]) FROM [EM-COMMUNICATION] as T WHERE T.[SUB-CODE] = N'DEALTWITH' AND DateDiff(MONTH,T.[CRT-DATE],GetDate()) = 1 AND [EM-COMMUNICATION].[CRT-USER] = T.[CRT-USER]) AS Dealt
FROM
[EM-COMMUNICATION]
WHERE
DateDiff(MONTH,[EM-COMMUNICATION].[CRT-DATE],GetDate()) = 1
GROUP BY
[EM-COMMUNICATION].[CRT-USER]

Filtering data in LINQ

I have an observable collection which would be bound to the silverlight datagrid, where i need to display a particular row based on the data present in the OC
ID Name Status Desc Role
--------------------------------
1 ABC 500 des 50
1 ABC 500 des 55
2 XYZ 502 des 57
in the above table there are duplicate values, i need to filter them in such a way that when (status = 500) i need to pick the row which has role as 50. or if the (status = 501) i need to pick the row which has role as 55. In any instant the status would remain same for a particular ID. My final data should look like the one below.
ID Name Status Desc Role
---------------------------------
1 ABC 500 des 50
2 XYZ 502 des 57
It's not a fun query by any means. There may be a better answer, but this should get you started. The trick here is that you'll need to change your orderby clause to meet your needs. I couldn't tell from your question whether you were trying to pick the min Role value, or were trying to convey something else, but that orderby clause is where your custom logic for picking the right record goes.
var results =
from a in DataVals
group a by new {a.ID, a.Name, a.Status, a.Desc} into g
select new {
g.Key.ID,
g.Key.Name,
g.Key.Status,
g.Key.Desc,
Role = (
from b in DataVals
where b.ID == g.Key.ID
&& b.Name == g.Key.Name
&& b.Status == g.Key.Status
&& b.Desc == g.Key.Desc
orderby b.Role
select b.Role
).Take(1).FirstOrDefault()
};

Resources