SQL Storing Age Range - sql

Im building an friendship site and I want to store restrictions for when a profile can be viewed.
Right now im using entity-attribute model to store restrictions. Examples of restrictions are what days a profile is available. The issue im having is i want to add a restriction on if a user can view a profile based on a users age range.
Im not sure if this approach is correct for storing, it feels really redundant but maybe im just being picky.
The two restrictions i want to add are minimum age and maximum age. Does this design seem like the correct approach?
User Attribute Entity Table
id(PK) | userid | attribute | entity
0 | 0 | 0 | 1
1 | 0 | 1 | 188
Attribute Table
id(PK) | attribute
0 | Minimum Age
1 | Maximum Age
2 | Contact Restricted Days
Entity Table
id(PK) | attribute_ID | Entity
0 | 0 | 18
1 | 0 | 19
.. | .. | ..
88 | 0 | 99
89 | 1 | 18
90 | 1 | 19
.. | .. | ..
188 | 1 | 99
199 | 2 | Monday
.. | .. | ..
205 | 2 | Sunday

I am posting this suggestion as an answer. But here is how I would have designed your table. Having id column in each table can cause headaches down the road, especially since you would need to explicitly define the column. Please compare the design below with your tables above:
User Attribute Entity Table
user_attr_entity_id(PK) | userid | attribute_id| entity_id
0 | 0 | 0 | 1
1 | 0 | 1 | 188
Attribute Table
attribute_id(PK) | attribute
0 | Minimum Age
1 | Maximum Age
2 | Contact Restricted Days
Entity Table
Entity_id(PK) | attribute_id | Entity_Value
0 | 0 | 18
1 | 0 | 19
.. | .. | ..
88 | 0 | 99
89 | 1 | 18
90 | 1 | 19
.. | .. | ..
188 | 1 | 99
199 | 2 | Monday
.. | .. | ..
205 | 2 | Sunday
UPDATE:
Seems like I must have misread your question initially. I think this design will work just fine. I tested using the query below:
SELECT userid
,a.attribute_id
,e.entity_id
,attribute
,entity_value
FROM user_attribute_entity_table uae
JOIN attribute_table a ON uae.attribute_id = a.attribute_id
JOIN entity_table e ON e.[entity_id] = uae.[entity_id]

If you're building for speed, i.e. if you need to optimize it for storing large amount of data, and if you know the "restrictions" (entitys) will not change a lot, then I'd say design it flatter/horizontally:
Id | UserId | Restriction1 | Restriction2 | Restriction3 | ..
This will however cost you flexibility, make it tougher on future growth/change and limit options.
Your original design is very flexible, and can be compared to a site with language dimensions - X amount of languages on Y amount of elements. Each element/entity should have it's own translation, and they should be easy to add/change.
However, design is somewhat about personal preference also. This is how I would solve it in a similar manner:
Users
-------------
UserId | Name
Attributes
-----------------------
AttributeId | Attribute
Entities
-----------------
EntityId | Entity/(or Value)
UserAttributeEntities
------------------------------------
Id | UserId (FK) | AttributeId (FK) | EntityId (FK)/or EntityValue
Entity, in my example, can either be it's own table (if values are resued often), or you can just put a column EntityValue direclty in the UserAttributeEntities table, and skip the Entities table.

Related

Microsoft Access - Query - Most Recent Entries

I have a main table in Microsoft Access that consists of a document number "AD", a revision number "Rev" and a "Decision Date". There is occasionally more than 1 revision for every AD and 1-2 decision dates for every revision. I want to create a query that selects the most recent entry by decision date, and create a new table that only contains the most recent entries. The purpose of this new table is to have only unique ADs, so that AD can be made a primary key and related to other objects in the database.
Current Table: tbl1_Complete_Data
+----+--------+-----+---------------+
| ID | AD | Rev | Decision Date |
+----+--------+-----+---------------+
| 1 |98-24-02| 0 | 1998-06-20 |
| 2 |98-24-02| 0 | 1998-06-21 |
| 3 |98-24-02| 1 | 1998-06-24 |
| 4 |98-24-02| 1 | 1998-06-24 |
| 5 |98-24-03| 0 | 1998-06-24 |
| 6 |98-24-03| 0 | 1998-06-24 |
+----+--------+-----+---------------+
New Table: tbl2_Report_Data
+----+--------+-----+---------------+
| ID | AD | Rev | Decision Date |
+----+--------+-----+---------------+
| 3 |98-24-02| 1 | 1998-06-24 |
| 5 |98-24-03| 0 | 1998-06-24 |
+----+--------+-----+---------------+
^The goal of this table is to get rid of ID.
Consider:
SELECT tbl1_Complete_Data.* FROM tbl1_Complete_Data WHERE ID IN (
SELECT TOP 1 ID FROM tbl1_Complete_Data AS Dupe
WHERE Dupe.AD = tbl1_Complete_Data.AD ORDER BY Dupe.DecisionDate DESC, Dupe.ID);
Strongly advise not to use spaces nor punctuation/special characters in naming convention.

Returning singular row/value from joined table date based on closest date

I have a Production Table and a Standing Data table. The relationship of Production to Standing Data is actually Many-To-Many which is different to how this relationship is usually represented (Many-to-One).
The standing data table holds a list of tasks and the score each task is worth. Tasks can appear multiple times with different "ValidFrom" dates for changing the score at different points in time. What I am trying to do is query the Production Table so that the TaskID is looked up in the table and uses the date it was logged to check what score it should return.
Here's an example of how I want the data to look:
Production Table:
+----------+------------+-------+-----------+--------+-------+
| RecordID | Date | EmpID | Reference | TaskID | Score |
+----------+------------+-------+-----------+--------+-------+
| 1 | 27/02/2020 | 1 | 123 | 1 | 1.5 |
| 2 | 27/02/2020 | 1 | 123 | 1 | 1.5 |
| 3 | 30/02/2020 | 1 | 123 | 1 | 2 |
| 4 | 31/02/2020 | 1 | 123 | 1 | 2 |
+----------+------------+-------+-----------+--------+-------+
Standing Data
+----------+--------+----------------+-------+
| RecordID | TaskID | DateActiveFrom | Score |
+----------+--------+----------------+-------+
| 1 | 1 | 01/02/2020 | 1.5 |
| 2 | 1 | 28/02/2020 | 2 |
+----------+--------+----------------+-------+
I have tried the below code but unfortunately due to multiple records meeting the criteria, the production data duplicates with two different scores per record:
SELECT p.[RecordID],
p.[Date],
p.[EmpID],
p.[Reference],
p.[TaskID],
s.[Score]
FROM ProductionTable as p
LEFT JOIN StandingDataTable as s
ON s.[TaskID] = p.[TaskID]
AND s.[DateActiveFrom] <= p.[Date];
What is the correct way to return the correct and singular/scalar Score value for this record based on the date?
You can use apply :
SELECT p.[RecordID], p.[Date], p.[EmpID], p.[Reference], p.[TaskID], s.[Score]
FROM ProductionTable as p OUTER APPLY
( SELECT TOP (1) s.[Score]
FROM StandingDataTable AS s
WHERE s.[TaskID] = p.[TaskID] AND
s.[DateActiveFrom] <= p.[Date]
ORDER BY S.DateActiveFrom DESC
) s;
You might want score basis on Record Level if so, change the where clause in apply.

Apply Limit for a Condition

I have a query that returns the credit notes (CN) and debit notes (DN) of an operation, each CN is accompanied by two or more DN (referenced by the field payment_plan_id). At the time of paging, for example I must bring 10 operations, that is 10 CN and their DN, but if I leave the limit at 10, it will also count the debit notes of the transaction that I must return in the query. So, it will only bring me 2, 3 or 4 operations depending on the number of DNs that accompany the credit note.
SELECT
value, installment, payment_plan_id, model,
creation_date, operation
FROM payment_plant
WHERE model != 'IMMEDIATE'
AND operation IN ('CN', 'DN')
AND creation_date BETWEEN '2017-06-12' AND '2017-07-12 23:59:59'
ORDER BY
model,
creation_date,
operation
LIMIT 10
OFFSET 1
Example of the table obviating some fields:
| id | payment_plan_id | value | installment | operation |
---------------------------------------------------------
| 1 | b3cdaede | 12 | 1 | NC |
| 2 | b3cdaede | 3.5 | 1 | ND |
| 3 | b3cdaede | 1.2 | 1 | ND |
| 4 | e1d7f051 | 36 | 1 | NC |
| 5 | e1d7f051 | 5.9 | 1 | ND |
| 6 | 00e6a0b4 | 15 | 1 | NC |
| 7 | 00e6a0b4 | 1 | 1 | ND |
| 8 | 00e6a0b4 | 3.6 | 1 | ND |
How can I limit the Limit so that it only consider the NCs?
Well, the query you give above doesn't do remotely what you describe. Assuming you actually want "the last 10 CN and their DN". You also don't explain what fields CN and DN have in common, so I'm going to assume that the fields are payment_plan_id and installment. Given that here's how you would get it:
WITH last_10_cn AS (
SELECT
value, installment, payment_plan_id, model,
creation_date
FROM payment_plant
WHERE model != 'IMMEDIATE'
AND operation = 'CN'
AND creation_date BETWEEN '2017-06-12' AND '2017-07-12 23:59:59'
ORDER BY
model,
creation_date,
operation
LIMIT 10
OFFSET 1 )
SELECT last_10_cn.*,
dn.value as dn_value, dn.model as dn_model,
dn.creation_date as dn_creation_date
FROM last_10_cn JOIN payment_plant as dn
ON last_10_cn.payment_plan_id = dn.payment_plan_id
AND last_10_cn.installment = dn.installment
ORDER BY
last_10_cn.model,
last_10_cn.creation_date,
last_10_cn.operation
dn.creation_date;
Adjust the above according to the actual join conditions and how you really want things to be sorted.
BTW, your table structure is what's giving you trouble here. DNs should really be a separate table with a foreign key to CNs. I realize that's not how most GLs do it, but the GL model predates relational databases.

How to add data or change schema to production database

I am new to working with databases and I want to make sure I understand the best way to add or remove data from a database without making a mess of any related data.
Here is a scenario I am working with:
I have a Tags table, with an Identity ID column. The Tags can be selected via the web application to categorize stories that are submitted by a user. When the database was first seeded; like tags were seeded in order together. As you can see all the Campuses (cities) were 1-4, the Colleges (subjects) are 5-7, and Populations are 8-11.
If this database is live in production and the client wants to add a new Campus (City) tag, what is the best way to do this?
All the other city tags are sort of organized at the top, it seems like the only option is to insert any new tags at to bottom of the table, where they will end up taking whatever the next ID available is. I suppose this is fine because the Display category column will allow us to know which categories these new tags actually belong to.
Is this typical? Is there better ways to set up the database or handle this situation such that everything remains more organized?
Thank you
+----+------------------+---------------+-----------------+--------------+--------+----------+
| ID | DisplayName | DisplayDetail | DisplayCategory | DisplayOrder | Active | ParentID |
+----+------------------+---------------+-----------------+--------------+--------+----------+
| 1 | Albany | NULL | 1 | 0 | 1 | NULL |
| 2 | Buffalo | NULL | 1 | 1 | 1 | NULL |
| 3 | New York City | NULL | 1 | 2 | 1 | NULL |
| 4 | Syracuse | NULL | 1 | 3 | 1 | NULL |
| 5 | Business | NULL | 2 | 0 | 1 | NULL |
| 6 | Dentistry | NULL | 2 | 1 | 1 | NULL |
| 7 | Law | NULL | 2 | 2 | 1 | NULL |
| 8 | Student-Athletes | NULL | 3 | 0 | 1 | NULL |
| 9 | Alumni | NULL | 3 | 1 | 1 | NULL |
| 10 | Faculty | NULL | 3 | 2 | 1 | NULL |
| 11 | Staff | NULL | 3 | 3 | 1 | NULL |
+----+------------------+---------------+-----------------+--------------+--------+----------+
The terms "top" and "bottom" which you use aren't really applicable. "Albany" isn't at the "Top" of the table - it's merely at the top of the specific view you see when you query the table without specifying a meaningful sort order. It defaults to a sort order based on the Id or an internal ROWID parameter, which isn't the logical way to show this data.
Data in the table isn't inherently ordered. If you want to view your tags organized by their category, simply order your query by DisplayCategory (and probably by DisplayOrder afterwards), and you'll see your data properly organized. You can even create a persistent View that sorts it that way for your convenience.

SQL multiple properties depending on other attributes

I have the following issue:
I am planning a database with trains. Each train has carriages which divides into compartment and non-compartment. Both of these types has three classes: 1,2,3, and all of them has different amount of places in compartment or in a row.
I could create the following table:
| type | class | seats in a row | rows | seats in a compartment | compartments |
| non-c| 1 | 3 | 18 | NULL | NULL |
| non-c| 2 | 4 | 22 | NULL | NULL |
| non-c| 3 | 5 | 25 | NULL | NULL |
| comp | 1 | NULL | NULL | 6 | 9 |
| comp | 2 | NULL | NULL | 8 | 10 |
| comp | 3 | NULL | NULL | 10 | 11 |
That is, I would set NULL when a property is not connected with a particular type (example number of places in a compartment for a non-compartment car), but in my opinion it is not good looking solution. Do you have any other ideas? Maybe two tables: non-compartment attributes and compartment attributes? However I think that better solution exists.
Like you said, break your design into tables that correspond to logical entities (normalization), that way you will have more scope to accommodate change and less redundant info.
Proposed design
Tables
Tbl_train(Id, other_train_info) -Stores only tran info
Tbl_Carriage(Id, trainid, carriagetypeid, other_carriage_info) - stores carriage info related to a train
Tbl_carriagetype_master(Id, type_desc, class, .. Etc) - stores all the static compartmental info