Relational algebra selection multiple conditions in same key - relational-database

Relational algebra, I have a table which only has pid in a loan schema. I want to know how to select and display pid100 and pid101. (Schema called LOAN) using correct syntax, without Union.

I found that I needed to use 'or', as I was selecting pid100 'Or' pid101 to display both id's.
σpid = "pid100" 'Or' "pid101"
σpid = "pid100" ∨ "pid101"
Credit to #AntC

Related

Why and how do these two queries both work

I have been trying to learn SQL using SQLBolts tutorial and for this exercise, I needed to write a query that showed the names of all the buildings with no employees using only LEFT JOIN. I had an answer different from the website and I am wondering why they both work. The only difference between our solutions is I put WHERE buildings is NULL vs SQL Bolts solution of WHERE role is null. If building value is null shouldn't that return a null value? Also same with role how does the Database know a building has a null value for role when there isn't a building attached to that role?
my query
SELECT building_name
FROM buildings
LEFT JOIN EMPLOYEES
ON buildings.building_name = EMPLOYEES.building
WHERE building IS NULL
SQL BOLT query
SELECT DISTINCT building_name, role
FROM buildings
LEFT JOIN employees
ON building_name = building
WHERE Role IS NULL
Buildings (table1) and Employees (table2)
Null Values from Database
Formally both queries are incorrect until complete tables structures are defined. When the query datasource includes more than one table copy then each column name must be specified with its table alias part. Exclusion - the column names used in USING clause or common columns when NATURAL JOIN is used, these columns may be used without table aliases. Backward exclusion - the query is used in compound statement, and local variables are present, in this case aliases must be used unconditionally.
If the tables structures claims that the queries texts are correct (all columns which have no table aliases are unique) then your query is more correct because it uses the column used in JOIN condition while testing for NULL. The "SQL BOLT query"may give wrong result if Role column is nullable and some rows contains NULL in this column.
If Role is defined as NOT NULL (directly or indirectly - for example, by according CHECK constraint, or it is a part of primary key) then both queries will give the same output.

How to join 2 sql tables where one table contains multiple values in a single column

Currently, this is what my SELECT code looks like:
SELECT student.stu_code, user.f_name, user.l_name
FROM user
INNER JOIN student
ON student.stu_code = user.user_id
INNER JOIN course
ON course.stu_code ?????;
Basically, to elaborate the student table inherits from user table, therefore I had user_id = stu_code. What I'm confused about is how to join course table with student table.
Let's say that the course table has a course code (PK), a few other attributes and a stu_code column, however, the student code column has multiple values inside a single column to represent that multiple students are taking the course and stored as VARCHAR.
Example: Student table has stu_code string value of '123' and course table has a stu_code with string value of '123, 246, 369'.
How would I go about joining these two tables together and separating the stu_code in the course table so that it represents 3 separate stu_code values -> i.e. '123', '246', '369'.
Any help is greatly appreciated!
however, the student code column has multiple values inside a single column to represent that multiple students are taking the course and stored as VARCHAR.
Your data model is broken. Put your effort into fixing the data model. You want a junction/association table courseStudents or perhaps enrolled, with columns like:
stu_code (foreign key to students)
course_code (foreign key to students)
enrollment_date
and so on
What is wrong with your data model? Here are a few things:
You are storing numbers as a string.
You are putting multiple values into a string column.
You cannot define foreign key relationships.
SQL has poor string handling capabilities.
SQL has a great way to store lists of things. It is not called "string". It is called "table".
Your data model is ~broken~ hindering you from elegant solutions.
You cannot join your two tables efficiently. While they might both contain strings they do not contain data with the same rules. Thus, you must transform the data in order to join them so you could do this in a few ways but one way is using regular expression function.
You can use it to evaluate a test on whether the stu_code matches the list of codes. Further, you can do this dynamically ... constructing the test string itself based upon values from the left and right
join based on REGEXP
SELECT student.stu_code, user.f_name, user.l_name
FROM user
INNER JOIN student
ON student.stu_code = user.user_id
INNER JOIN course
ON student.stu_code REGEXP CONCAT('[[:&lt:]]',course.stu_code,'[[:&gt:]]')
Assuming tables and data:
Student
- - - -
stu_code
123
Course
- - - -
stu_code
'123, 246, 369'
Example:
http://sqlfiddle.com/#!9/672b57f/4
about the regular expression
in mysql the regex syntax can be a little bit different. [[:<:]] is the character class in spencer notation for word boundary.
if you have a new enough version of mysql/mariadb you can use more typical ICU notation of \b.
more about that here : https://dev.mysql.com/doc/refman/8.0/en/regexp.html
about efficiency
in large datasets the performance will be awful. you will have to scan all records and you will have to perform the function on all of them. In a large set you might get some gains by joining on like first (which is faster than regexp). This will be much faster at filtering-out and then the regexp can deal with filtering-in.
Perhaps your model was based upon an assumption of having a courses table with very few rows?
It ironic because you have made your course table unnecessarily large. You would actually be better off with an intermediary table that represents the many-to-many nature (the fact that students can take many courses and courses can have many students) with 1 row per unique relationship. While this table would be an order of magnitude "longer" it would be leaner and it could be indexed and query performance would be faster.
The courses table does not need to have any awareness of the student list and thus you can alter courses by removing courses.stu_code once you change the model (aside: It might be useful if courses cached a hint of the expected student count for that course)
possible link table
would be a new table like this (note how it only ever needs these 2 columns)
stu_course_lnk
- - - - - - - -
stu_code course_id
123 ABC
124 ABC
...
123 XYZ
...
124 LMN
then you add joins of
...
student.stu_code = stu_course_lnk.stu_code
and
stu_course_lnk.course_id = course.id
...

group_concat on two different tables gives duplicate result on second table

consider three entities as student, course, subject
Below are the associations -
student has_many courses,
student has_many subjects.
Now i want to fetch student records with subject names and course names using mysql group_concat, left join on courses, left join on subjects and group_by student_id.
Problem is that group_concat('subjects.name') as subject_names gives me duplicate entries of subjects but group_concat('students.name') as student_names gives unique names.
Why ??
The 2 left joins are multiplying rows via Cartesian product of the child rows per student
Example
Student 1 has 3 courses and 2 subjects
Generates 6 rows for Student 1
Gives one course value per subject = each course repeated twice
Gives one subject value per course = each subject repeated thrice
To fix:
Option 1: Use GROUP_CONCAT(DISTINCT ...) as per MySQL docs
In MySQL, you can get the concatenated values of expression combinations. To eliminate duplicate values, use the DISTINCT clause.
Option 2: Use a UNION ALL + derived table
SELECT
Student, MAX(CourseConcat), MAX(SubjectConcat)
FROM
(
-- 2 separate SELECTs here
.. student LEFT JOIN course ...
UNION ALL
.. student LEFT JOIN subjects...
) T
GROUP BY
Student
The 2nd option may be better albeit more complex because you have less intermediate rows to process with DISTINCT
Following you logic, group_concat('subjects.name') as subject_names gives you duplicate entries because there's possibly more than 1 subject for each student, so you're getting a duplicate record for every student record on the subject table, while group_concat('students.name') as student_names (I presume) has 1 record per student.
I know I'm probably driving this a bit too off topic, but because searching answer from google have directed me here for several times already I'd like to share my solution, for a bit more complicated similar problem.
The GROUP_CONCAT(DISTINCT ...) solution as gbn pointed out, is great, until you actually have multiple equal values or almost equal like á and a.
I left out the distinct keyword from query and solved the problem with PHP. If you only need to distinguish á from a, simple array_unique will do the trick.
Unfortunately I was not so lucky and I also had exactly equal values which I needed to keep. Consider sample values returned from database query group_concat field exploded into array:
$values = array( 'Value1','Value1','Value2','Value2','Value2','Value2' );
Now somehow distinguish how many duplicates are you dealing with. I did the following:
$x=0;
$first = reset($values);
while($first === $values[$x]) $x++;
Above solution works only if your actual first and second value is never same, which in my case was true. If that's not the case with you, figure out some other way to know how many duplicates are you dealing with.
Finally just unset all extra values with a help of modulo:
foreach($values as $k => $v){
if($k%$x !== 0) unset($values[$k]);
}
Thats it. Printing $values now will give you:
Array
(
[0] => Value1
[2] => Value2
[4] => Value2
)

New to MySQL looking for Many-to-Many relationship query

Some background I have a set of data that represents the alchemy ingredients and their effects from Skyrim. If you're unfamiliar with this you can combine 2-4 ingredients to make a potion. Each ingredient has 4 effects. If any effects between ingredients are the same it will make that type of potion. I've identified this as a many-to-many relationship and I set up my tables like so:
ingredients: ing_id (key), ing_name, (other supplemental info)
effects: eff_id (key), eff_name
ing_eff_xref: eff_id, ing_id
I would like to input 2 or more available ingredients and return possible combinations without knowing what the effects are. My sql experience is pretty much limited to phpmyadmin and simple select queries. I guess my questions are: is this the right way to structure the tables for this type of relationship, do I need to set foreign keys if I don't plan on updating the tables, and is there a query that can take a set of ing_names and return only eff_names that intersect?
Here is the mysqldump of the db if anyone is interested: http://dl.dropbox.com/u/59699040/alchemy_db.sql
is this the right way to structure the tables for this type of relationship?
Yes, but then you don't need to have effect1 through effect4 on the ingredient table.
do I need to set foreign keys if I don't plan on updating the tables?
Yes. The only way for you to get the data that you're after is by JOINing three tables together. Without foreign keys (or more specifically, appropriate indexes), that may not perform well on queries. Of course you do have a small number of rows overall, but using foreign keys is a good practice to follow in this type of scenario.
is there a query that can take a set of ing_names and return only
eff_names that intersect?
I think you're after something like this:
SELECT e.eff_name
FROM ingredients i
INNER JOIN ing_eff_xref ie ON ie.ing_id = i.ing_id
INNER JOIN effects e ON e.eff_id = ie.eff_id
WHERE i.ing_name = 'Abecean Longfin ';
If you need to see effects for multiple ingredients, you could adjust your WHERE clause, like this:
WHERE i.ing_name IN ('Abecean Longfin ','Eye of Sabre Cat ','Bear Claws ');
You'll probably not want duplicate effects, so you could do a SELECT DISTINCT to eliminate those.
Can potion effects stack in Skyrim? If they in can stack, then you can do a GROUP BY query with a COUNT to get the stacked value of each effect:
SELECT e.eff_name, count(*) as value
FROM ingredients i
INNER JOIN ing_eff_xref ie ON ie.ing_id = i.ing_id
INNER JOIN effects e ON e.eff_id = ie.eff_id
WHERE i.ing_name IN ('Eye of Sabre Cat ','Bear Claws ')
GROUP BY e.eff_name;
This query will list 6 effects with a value of 1, and "Restore Stamina" will have a value of 2. Not sure if Skyrim potions work this way or not, but it was just an extra thought.

How do I make the rows of a lookup table into the columns of a query?

I have three tables: students, interests, and interest_lookup.
Students has the cols student_id and name.
Interests has the cols interest_id and interest_name.
Interest_lookup has the cols student_id and interest_id.
To find out what interests a student has I do
select interests.interest_name from `students`
inner join `interest_lookup`
on interest_lookup.student_id = students.student_id
inner join `interests`
on interests.interest_id = interest_lookup.interest_id
What I want to do is get a result set like
student_id | students.name | interest_a | interest_b | ...
where the column name 'interest_a' is a value in interests.name and
the interest_ columns are 0 or 1 such that the value is 1 when
there is a record in interest_lookup for the given
student_id and interest_id and 0 when there is not.
Each entry in the interests table must appear as a column name.
I can do this with subselects (which is super slow) or by making a bunch of joins, but both of these really require that I first select all the records from interests and write out a dynamic query.
You're doing an operation called a pivot. #Slider345 linked to (prior to editing his answer) another SO post about doing it in Microsoft SQL Server. Microsoft has its own special syntax to do this, but MySQL does not.
You can do something like this:
SELECT s.student_id, s.name,
SUM(i.name = 'a') AS interest_a,
SUM(i.name = 'b') AS interest_b,
SUM(i.name = 'c') AS interest_c
FROM students s
INNER JOIN interest_lookup l USING (student_id)
INNER JOIN interests i USING (interest_id)
GROUP BY s.student_id;
What you cannot do, in MySQL or Microsoft or anything else, is automatically populate columns so that the presence of data expands the number of columns.
Columns of an SQL query must be fixed and hard-coded at the time you prepare the query.
If you don't know the list of interests at the time you code the query, or you need it to adapt to changing lists of interest, you'll have to fetch the interests as rows and post-process these rows in your application.
What your trying to do sounds like a pivot.
Most solutions seem to revolve around one of the following approaches:
Creating a dynamic query, as in Is there a way to pivot rows to columns in MySQL without using CASE?
Selecting all the attribute columns, as in How to pivot a MySQL entity-attribute-value schema
Or, identifying the columns and using either a CASE statement or a user defined function as in pivot in mysql queries
I don't think this is possible. Actually I think this is just a matter of data representatioin. I would try to use a component to display the data that would allow me to pivot the data (for instance, the same way you do on excel, open office's calc, etc).
To take it one step further, you should think again why you need this and probably try to solve it in the application not in the database.
I know this doesn't help much but it's the best I can think of :(