Constraints on database - database

I have a following table called students. If I want to put a constraint -- for each state, there are no more than 2 students can come from this same state, how could I express such a constraint in relational algebra?
name | hometown | gender
-----------------------------------
Bob | NYC | male
Alice | Washington D.C| female
Linda | London | female
Peter | Miami | male
Amy | Philadelphia | female
Lucy | NYC | female
James | Albany | male
Jason | Los Angeles | male
Cindy | Salt Lake City| female
Jackson| Princeton | male
Judy | Seattle | female
Marcia | San Francisco | female
Steve | NYC | male
John | Miami | male

You're going to need a relation providing the state for each town. Join that with this table (on town) and that will give you the state. Then group that result by state and count the occurrences, constraining the count to be less than or equal to two. As you haven't provided the required relation or relation names, I can't give you the algebra, but you should find this helps you get your homework done.

Related

Track changes made in a record and get the version of the record at the given point of time in SQL Server

I need to track chages made in a record and able to get the any version of the record at given point of time.
Consider, I have the following contact table.
--------------------------
| contact |
--------------------------
| id |
| name |
| phone |
| email |
| city |
| country |
| create_by |
| created_at |
--------------------------
I want to achieve following things on the above table.
I want to track all the changes made in the records over the time.
I want to find the version of the record at the given point of time.
The original record should stay as it is (without any update).
For example,
So, I have created following table to track all the changes.
--------------------------
| contact_history |
--------------------------
| contact_id |
| field_name |
| old_value |
| new_value |
| modified_by |
| modified_at |
--------------------------
With this data model,
To create a new contact record, we will create the record in the contact table.
To Update a record, we will create a new record in the contact_history record with the field name, new value, current time and who modified the record.
In this way I can able to track, what change was made and who made the change and the same time the original record will stay as it is without any change.
What I am trying to achieve is to find the version of all the contact records at a given point of time.
For example, consider the following record.
Contact table
------------------------------------------------------------------------------------------------
| id | name | phone | email | city | country | created_by | created_at |
------------------------------------------------------------------------------------------------
| 1 | Steve | 1111111 | steve#abc.com | NY | USA | John | 2019-04-01 13:17:49.417 |
------------------------------------------------------------------------------------------------
Each update will be created as a history record in the contact_history table.
contact_history
-------------------------------------------------------------------------------------------------------
| contact_id | field_name | old_value | new_value | modified_at | modified_by |
-------------------------------------------------------------------------------------------------------
| 1 | email | steve#abc.com | steve#changed.com | 2019-04-02 08:19:49.213 | Arnold |
| 2 | city | NY | LA | 2019-04-03 12:48:37.568 | John |
| 3 | city | LA | SF | 2019-04-04 25:25:19.715 | John |
-------------------------------------------------------------------------------------------------------
When I need the version of the contact record before 2019-04-02 I should able to get the following,
------------------------------------------------------------------------------------------------
| id | name | phone | email | city | country | created_by | created_at |
------------------------------------------------------------------------------------------------
| 1 | Steve | 1111111 | steve#abc.com | NY | USA | John | 2019-04-01 13:17:49.417 |
------------------------------------------------------------------------------------------------
When I need the version of the contact record before 2019-04-03 I should able to get the following,
----------------------------------------------------------------------------------------------------
| id | name | phone | email | city | country | created_by | created_at |
----------------------------------------------------------------------------------------------------
| 1 | Steve | 1111111 | steve#changed.com | NY | USA | John | 2019-04-01 13:17:49.417 |
----------------------------------------------------------------------------------------------------
When I wanted the latest version of the same record I should able to get,
----------------------------------------------------------------------------------------------------
| id | name | phone | email | city | country | created_by | created_at |
----------------------------------------------------------------------------------------------------
| 1 | Steve | 1111111 | steve#changed.com | SF | USA | John | 2019-04-01 13:17:49.417 |
----------------------------------------------------------------------------------------------------
The example is given for only one record. It can be applicable for multiple contact records.
How can I write a efficient query to achieve this in SQL Server.
Simple. Use a TOP 1 query to get the most recent record before the date you want to filter on.
Unfortunately, due to the data model you've chosen to use, you'll need to do a TOP 1 subquery for every field in your result set that can be in your history table.
SELECT id,
(SELECT TOP 1 new_value ... WHERE field_name='Name' ... ) AS Name,
etc...

How to create a variable that adds values of a certain variable by group?

I have an SPSS data set that looks like the one below. How can I compute the basket size based on the invoice no (i.e. the total number of items per invoice)? The cases are listed product wise.
Item Code | Description | Invoice No | Invoice Qty
1234 | Bread |12345678 | 2
1543 | Bun |12345678 | 3
1234 | Bread |98765432 | 6
So the end result should be:
Item Code | Description | Invoice No | Invoice Qty | Basket Size
1234 | Bread |12345678 | 2 | 5
1543 | Bun |12345678 | 3 | 5
1234 | Bread |98765432 | 6 | 6
Aggregating using invoice as the break variable is the way to go.
aggregate /break = InvoiceNo /BasketSize = sum(InvoiceQty).
Gives:
ItemCode Description InvoiceNo InvoiceQty BasketSize
1234 Bread 12345678 2 5.00
1543 Bun 12345678 3 5.00
1234 Bread 98765432 6 6.00

sql server join 3 tables, not in other column of third table

So, i have two tables which are connected by a third one.
Something like:
First Table:
+------------+--------------+--------------+------------+
| box_column | code_column | from_column | to_column |
+------------+--------------+--------------+------------+
| 12345 | sdsad1 | madrid | london |
+------------+--------------+--------------+------------+
Second Table:
+-------------+--------------+
| code_column | truck_column |
+-------------+--------------+
| sdsad1 | truck1 |
| sdsad1 | truck2 |
| sdsad1 | truck3 |
+-------------+--------------+
Third Table:
+--------------+-------------+-----------+
| truck_column | from_column | to_column |
+--------------+-------------+-----------+
| truck1 | madrid | paris |
| truck2 | paris | calais |
| truck3 | calais | london |
+--------------+-------------+-----------+
after having a join, just having the number of the box, is there any way i can distinct the trucks that make the last leg (third table have london in the to_column) and the others?
That is not very difficult to do, it's actually quite basic:
SELECT *
FROM dbo.code c
INNER JOIN dbo.jointable j on c.code = j.code
INNER JOIN dbo.truck t on j.truck = t.truck
WHERE c.box = 12345
AND c.[to] = t.[to]
Where code is your first table, jointable is your second table, and truck is your third table.
The output of this query is:
box code from to code truck truck from to
---------------------------------------------------------------------
12345 sdsad1 madrid london sdsad1 truck3 truck3 calais london
To get only the truck as output, replace
SELECT *
with
SELECT t.truck
Last but not least: I'm not seeing any primary keys, nor foreign keys in your model. Maybe you left it out. If not, please use keys and constraints.

SQL SELECT Query with returning hierarchical data

I have got a table which goes something like this:
State_id | County_id| City_id | Name | Additional_Name
-----------------------------------------------------------------
1 | 0 | 0 | California | State
1 | 1 | 0 | Los Angeles | County
1 | 1 | 1 | Los Angeles | City
1 | 2 | 0 | San Diego | County
1 | 2 | 1 | San Diego | City
2 | 0 | 0 | Texas | State
2 | 1 | 0 | Harris | County
2 | 1 | 1 | Houston | City
It goes on for 10,000 rows. What I'm trying to accomplish is to build a SELECT statement which will result in:
State | County | City
-------------------------------------------
California | Los Angeles | Los Angeles
California | San Diego | San Diego
Texas | Harris | Houston
As you can see i want to select every city and display it's state, and county. The state_id, county_id, city_id and Additonal_Name columns should be essential in solving this problem, but i have no idea how to use them.
This will get your data, using your current table structure:
SELECT t2.[Name] AS [State]
,t3.[Name] AS County
,t1.[Name] AS City
FROM MyTable t1
JOIN MyTable t2 -- get state
ON t2.State_id = t1.State_id
AND t2.County_id = 0
AND t2.City_id = 0
JOIN MyTable t3 -- get county
ON t3.County_id = t1.County_id
AND t3.State_id = t1.State_id
AND t3.City_id = 0
WHERE t1.City_id > 0 --(or use t1.Additional_Name = 'City'
But Really:
You should be normalizing your database into three separate tables:
City
State
County
This will make using your data, and writing queries - muchh simpler and readable.
The tables would look something like this:
State
ID Name
1 California
2 Texas
County
ID Name
1 Los Angeles
2 San Diego
3 Harris
City
ID StateID CountyID Name
1 1 1 Los Angeles
2 1 2 San Diego
3 2 3 Houston
Hopefully you can see how much easier that is to manage things. You can choose to add a StateID to the County table to normalize further. But I'll keep my answer short.
Select the same data from these new tables with a similar query:
SELECT state.[Name] AS [State]
,county.[Name] AS County
,city.[Name] AS City
FROM MyCity city
JOIN MyState state ON state.ID = city.StateID -- get state
JOIN MyCounty county ON county.ID = city.CountyID -- get county

Meet both WHERE criteria - T-SQL

I need help refining the WHERE clause on a query for the table below:
Key | Name | Role | Location
111 | Bob | Manager | All Locations
222 | Jim | Manager | All Locations
333 | Tim | Sales | Washington
444 | Roy | Sales | New York
555 | Lee | Sales | All Locations
666 | Gus | Sales | All Locations
777 | Joe | Admin | All Locations
888 | Jen | Admin | New York
I need to exclude all the 'All Locations' records, but keep the 'All Locations' records where the Role is Manager. To get the desired result:
Key | Name | Role | Location
111 | Bob | Manager | All Locations
222 | Jim | Manager | All Locations
333 | Tim | Sales | Washington
444 | Roy | Sales | New York
888 | Jen | Admin | New York
I feel that the query below would exclude all the All Locations records, including the Manager ones.
SELECT * FROM Table
WHERE (Location <> 'All Locations' AND Role <> 'Manager')
You will want to expand the WHERE:
select *
from yourtable
where
(
role = 'Manager'
and location = 'All Locations'
)
or
(
location <> 'All Locations'
)
See SQL Fiddle with Demo
Returns the result:
| KEY | NAME | ROLE | LOCATION |
----------------------------------------
| 111 | Bob | Manager | All Locations |
| 222 | Jim | Manager | All Locations |
| 333 | Tim | Sales | Washington |
| 444 | Roy | Sales | New York |
| 888 | Jen | Admin | New York |
SELECT * FROM Table
WHERE (Location != 'All Locations' OR (Location = 'All Locations' AND Role = 'Manager')
You say "exclude all the 'All Locations' records, but keep the 'All Locations' records where the Role is Manager." Doesn't that mean exclude the All location records where role is not manager? i.e., don't you want records 111 and 222 to be included ??
From De Morgans Law, Not A And Not B is equivalent to Not (A Or B)
In your case , one predicate is positive and the other negative, so De Morgan would read:
Not (A And not B) <=> (Not A or B), i.e.,
That means include all records that are either Not All Locations or Manager .
If so then what you need is:
... Where Location != 'All Locations' Or Role = 'Manager'
You should use OR instead of AND
SELECT * FROM Table
WHERE (Location <> 'All Locations' OR Role = 'Manager')

Resources