Aggregate function in Tuple Relational Calculus - sql

How do you translate a COUNT or a GROUP BY or any other aggregate function you find in SQL into TRC, i can't find any way on internet.
So I have a table User
+----+----------+--+
| | User | |
+----+----------+--+
| pk | email | |
| | password | |
| | ... | |
+----+----------+--+
And a table frienship
+----+-------------+--+
| | FriendShip | |
+----+-------------+--+
| pk | user1_email | |
| pk | user2_email | |
| | date | |
| | accepted | |
+----+-------------+--+
And the following query in SQL:
SELECT *
FROM user u
LEFT OUTER JOIN friendship f ON (f.user1_email = u.email
OR f.user2_email = u.email)
GROUP BY u.email
HAVING COUNT(u.email) < 3
I would like to transform this query into tuple relational Calculus, the JOIN and the SELECT are pretty straightforward, but for the GROUP BY and the COUNT I don't know.
Thanks,

As Lennart says, it's not possible to express those functions so, I decided to transform the count in another way.
First let's assert the following predicate:
Then we can say that having 2 or less friends, is having 0 friends, 1, or 2. To have 1 friend is like saying that there exists a friend (friend1) for wich Friends(me, friend1) is true.
To have 2 friends, you must have 1 friend and another, different. And finally you must not have any more friend.
All this can be express like this:

I don't think you can express aggregate functions in neither TRC nor RA. However, there have been proposals to extend them, see for example:
http://cis.csuohio.edu/~matos/notes/cis-612/NestedRelations/Extending%20Relational%20Algebra%20and%20Relational%20Calculus%20with%20Se.pdf

Related

Find sequence of choice in a column

There is a table where user_id is for each test taker, and choice is the answer for all the three questions. I would like to get all the different sequence of choices that test taker made and count the sequence. Is there a way to write sql query to achieve this? Thanks
----------------------------------
| user_id | Choice |
----------------------------------
| 1 | a |
----------------------------------
| 1 | b |
----------------------------------
| 1 | c |
----------------------------------
| 2 | b |
----------------------------------
| 2 | c |
----------------------------------
| 2 | a |
----------------------------------
Desire answer:
----------------------------------
| choice | count |
----------------------------------
| a,b,c | 1 |
----------------------------------
| b,c,a | 1 |
-----------------------------------
In BigQuery, you can use aggregation functions:
select choices, count(*)
from (select string_agg(choice order by ?) as choices, user_id
from t
group by user_id
) t
group by choices;
The ? is for the column that specifies the ordering of the table. Remember: tables represent unordered sets, so without such a column the choices can be in any order.
You can do something similar in SQL Server 2017+ using string_agg(). In earlier versions, you have to use an XML method, which is rather unpleasant.

How to select table with a concatenated column?

I have the following data:
select * from art_skills_table;
+----+------+---------------------------+
| ID | Name | skills |
+----+------+---------------------------|
| 1 | Anna | ["painting","photography"]|
| 2 | Bob | ["drawing","sculpting"] |
| 3 | Cat | ["pastel"] |
+----+------+---------------------------+
select * from computer_table;
+------+------+-------------------------+
| ID | Name | skills |
+------+------+-------------------------+
| 1 | Anna | ["word","typing"] |
| 2 | Cat | ["code","editing"] |
| 3 | Bob | ["excel","code"] |
+------+------+-------------------------+
I would like to write an SQL statement which results in the following table.
+------+------+-----------------------------------------------+
| ID | Name | skills |
+------+------+-----------------------------------------------+
| 1 | Anna | ["painting","photography","word","typing"] |
| 2 | Bob | ["drawing","sculpting","excel","code"] |
| 3 | Cat | ["pastel","code","editing"] |
+------+------+-----------------------------------------------+
I've tried something like SELECT * from art_skills_table LEFT JOIN computer_table ON name. However it doesn't give what I need. I've read about array_cat but I'm having a bit of trouble implementing it.
if the skills column from both tables are arrays, then you should be able to get away with this:
SELECT a.ID, a.name, array_cat(a.skills, c.skills)
FROM art_skills_table a LEFT JOIN computer_table c
ON c.id = a.id
That said, While you used LEFT join in your sample, I think either an INNER or FULL (OUTER) join might serve you better.
First, i wondered why the data are stored in such a model.
Was of the opinion that NoSQL databases lack ability for joins and ...
... a semantic triple would be in the form of subject–predicate–object.
... a Key-value (KV) stores use associative arrays.
... a relational database would be normalized.
A few information about the use case would have helped.
Nevertheless, you can select the data with CONCAT and REPLACE for the desired form.
SELECT art_skills_table.ID, computer_table.name,
CONCAT(
REPLACE(art_skills_table.skills, '}',','),
REPLACE(computer_table.skills, '{','')
)
FROM art_skills_table JOIN computer_table ON art_skills_table.ID = computer_table.ID
The query returns the following result:
+----+------+--------------------------------------------+
| ID | Name | Skills |
+----+------+--------------------------------------------+
| 1 | Anna | {"painting","photography","word","typing"} |
| 2 | Cat | {"drawing","sculpting","code","editing"} |
| 3 | Bob | {"pastel","excel","code"} |
+----+------+--------------------------------------------+
I've used the ID for the JOIN, even though Bob has different values.
The JOIN should probably be done over the name.
JOIN computer_table ON art_skills_table.Name = computer_table.Name
BTW, you need to tell us what SQL engine you're running on.

Sql data from row to column with reference to another column

Parent table
+====+===========+
| id | firstname |
+====+===========+
| 1 | abc |
+----+-----------+
| 2 | bcd |
+----+-----------+
| 3 | cde |
+----+-----------+
StudentRelationship table
+==========+==========+===========+
| relation | parentid | studentid |
+==========+==========+===========+
| father | 1 | s0001 |
+----------+----------+-----------+
| mother | 2 | s0001 |
+----------+----------+-----------+
| father | 3 | s0002 |
+----------+----------+-----------+
STUDENT table
+=======+===========+==========+=========+======+
| id | firstname | lastname | address | sex |
+=======+===========+==========+=========+======+
| s0001 | shdj | khb | jxx | male |
+-------+-----------+----------+---------+------+
It would be great if you could help me create a query which will return studentid ,name,father name,mother name,sex,address.
Based on what you've posted, then updated in your comments, I think this should work for you. I am sure someone with more advanced SQL skills can post a more elegant way to do this. But this is what I came up with:
SELECT DISTINCT cte.studentid
,studentFirstName
,studentLastName
,father.fatherFirstName
,mother.motherFirstName
,sex
,address
FROM cte
LEFT JOIN father ON cte.studentid = father.studentid
LEFT JOIN mother ON cte.studentid = mother.studentid
The following is an example where a student (Jeff Jones) has two fathers (let's say one of them is the step-father):
A few recommendations here:
Take a course on SQL syntax fundamentals (any type MySQL, T-SQL, etc..)
Read about FROM and JOIN
When posting your question here, the table examples should have better test data. "asdfkj", "shdsf", "Asdjkfdjkf" are horribly hard to
use to test code against because there is no context of what you are
looking at. I realize you are just posting an example, and the context
of the rows is partly insignificant, but it just makes for easier
question answering, and doesn't scare off people who would want to
answer your question.
Here is an DEMO you can play with, that has reasonable data in the fields you've mentioned.

Using Join, select values from multiple SQL tables without a foreign key

I have a database containing two tables Team and User.
Every team can have one or two users in it.
I wish to select an output of the table Team such that information of the both the users from the table User are included.
It'll be easier to understand once I define the table structures.
Table Team:
+-----------+-------------------+-------------------+-----------------+
| team_name | user_one | user_two | team_note |
+-----------+-------------------+-------------------+-----------------+
| Team one | skuhsa#jdds.dfd | kgihse#kdhf.dfj | one to twenty |
| Team two | dsjgknsz#djfd.fkg | | three to thirty |
+-----------+-------------------+-------------------+-----------------+
Table User:
+-------------------+-----------+-----------------+
| email | user_name | user_note |
+-------------------+-----------+-----------------+
| skuhsa#jdds.dfd | skuhsaone | gimme money |
| kgihse#kdhf.dfj | kgihse | drop it |
| dsjgknsz#djfd.fkg | dsjgknsz | just eat it |
+-------------------+-----------+-----------------+
The output I'm looking for goes like this.
+-----------+--------------------+-----------+------------------+-----------+-----------------+
| team_name | user_one | user_name | user_two | user_name | team_note |
+-----------+--------------------+-----------+------------------+-----------+-----------------+
| Team one | skuhsa#jdds.dfd | skuhsaone | kgihse#kdhf.dfj | kgihse | one to twenty |
+-----------+--------------------+-----------+------------------+-----------+-----------------+
| Team two | dsjgknsz#djfd.fkg | dsjgknsz | | | three to thirty |
+-----------+--------------------+-----------+------------------+-----------+-----------------+
I have a good feeling that it can be done easily, but right now I'm trying all sorts of JOINs and stuff and ending up with duplicate results or rows.
If there is any PostgreSQL specific ways to do it, it'd just fine with me.
SELECT * FROM Team LEFT JOIN User ON Team.user_one=User.email works, but how do I select only the columns I want? i.e., how will it differentiate b/w columns for the first and the second user?
A couple of left joins should do the trick:
SELECT team_name, user_one, u1.user_name, user_two, u2.user_name, team_note
FROM team t
LEFT JOIN user u1 ON t.user_one = u1.email
LEFT JOIN user u2 ON t.user_one = u2.email
The answer is actually quite simple, as the comment says simply use a LEFT join, this could by saying LEFT JOIN [TableName]
SELECT * FROM [Team]
LEFT JOIN [User]
Also take a look at
http://www.w3schools.com/sql/sql_join_left.asp
Edit:
Your second option would be to do this:
SELECT * FROM [Team]
UNION
SELECT * FROM [User]
An inner join or a left join can be used to extract data from both tables.
But the result of the query will be more similar to the following:
+-----------+--------------------+-----------+-----------------+
| team_name | user_one | user_name | team_note |
+-----------+--------------------+-----------+-----------------+
| Team one | skuhsa#jdds.dfd | skuhsa | one to twenty |
+-----------+--------------------+-----------+-----------------+
| Team one | skuhsa#jdds.dfd | kgihse | one to twenty |
+-----------+--------------------+-----------+-----------------+
| Team two | dsjgknsz#djfd.fkg | dsjgknsz | three to thirty |
+-----------+--------------------+-----------+-----------------+
This is the best solution because you don't know how many users are present in a team what happens if they are 3 (or 100?) ? But it is different from your expectations so check if you agree with that form.
firstly you sould re-think aout tables. What is the meaning wirting user e-mails in two different tables? In the end you will get all of them in one table.
Other than that you shoul look at foreign key. Foreign keys link tables together. I think your tables must be like.
+-----------+-------------------+-------------------+-
| team_name | team_note | team_id(Primary key)
+-----------+-------------------+-------------------+
| Team one | one to twenty | 1
| Team two | stuff | 2
+-----------+-------------------+-------------------+
+-------------------+-----------+-----------------+-------------------------------------------------
| email | user_name | user_note | f_team_id(foreign_key) to team.team_id
+-------------------+-----------+-----------------+--------------------------------------
| skuhsa#jdds.dfd | skuhsaone | gimme money | 1
| kgihse#kdhf.dfj | kgihse | drop it | 1
| dsjgknsz#djfd.fkg | dsjgknsz | just eat it | 2
+-------------------+-----------+-----------------+------------------------------------------
After that
SELECT * FROM team JOIN user ON user.f_team_id=team.team_id; OR
SELECT * FROM team, user WHERE user.f_team_id=team.team_id;

Grouped string aggregation / LISTAGG for SQL Server

I'm sure this has been asked but I can't quite find the right search terms.
Given a schema like this:
| CarMakeID | CarMake
------------------------
| 1 | SuperCars
| 2 | MehCars
| CarMakeID | CarModelID | CarModel
-----------------------------------------
| 1 | 1 | Zoom
| 2 | 1 | Wow
| 3 | 1 | Awesome
| 4 | 2 | Mediocrity
| 5 | 2 | YoureSettling
I want to produce a dataset like this:
| CarMakeID | CarMake | CarModels
---------------------------------------------
| 1 | SuperCars | Zoom, Wow, Awesome
| 2 | MehCars | Mediocrity, YoureSettling
What do I do in place of 'AGG' for strings in SQL Server in the following style query?
SELECT *,
(SELECT AGG(CarModel)
FROM CarModels model
WHERE model.CarMakeID = make.CarMakeID
GROUP BY make.CarMakeID) as CarMakes
FROM CarMakes make
http://www.simple-talk.com/sql/t-sql-programming/concatenating-row-values-in-transact-sql/
It is an interesting problem in Transact SQL, for which there are a number of solutions and considerable debate. How do you go about producing a summary result in which a distinguishing column from each row in each particular category is listed in a 'aggregate' column? A simple, and intuitive way of displaying data is surprisingly difficult to achieve. Anith Sen gives a summary of different ways, and offers words of caution over the one you choose...
If it is SQL Server 2017 or SQL Server VNext, Azure SQL database you can use String_agg as below:
SELECT make.CarMakeId, make.CarMake,
CarModels = string_agg(model.CarModel, ', ')
FROM CarModels model
INNER JOIN CarMakes make
ON model.CarMakeId = make.CarMakeId
GROUP BY make.CarMakeId, make.CarMake
Output:
+-----------+-----------+---------------------------+
| CarMakeId | CarMake | CarModels |
+-----------+-----------+---------------------------+
| 1 | SuperCars | Zoom, Wow, Awesome |
| 2 | MehCars | Mediocrity, YoureSettling |
+-----------+-----------+---------------------------+