Confirm result of left join sybase query - sql

I need some help with sybase sql syntax. Going through some example questions after many years of being away from sql. Given the following two sybase table structures and data:
Table name: users
| name | salary |
| joe | 100000 |
| nick | 10000 |
Table name: user_data
| name | percent |
| joe | 0.67 |
and the following query:
select u.name, ud.percent from users u, user_data ud where u.name *= ud..name
am I right in thinking that the output will be:
| name | percent |
| joe | 0.67 |
| nick | NULL |
based on the reasoning that the *= means left join?
The other question I had is what does the '..' mean in the ud..name?
Thanks.

Yes that is older JOIN syntax that has been replaced with ANSI JOIN syntax. The query should be written:
select u.name, ud.percent
from users u
left join user_data ud
on u.name = ud.name
This query will return all users in your table even if there is not a matching row in the user_data table. For those rows not in the table a null will be returned.

Related

Why the output of a SELECT can be another SELECT?

I am rather confused about the following SQL query:
SELECT (SELECT S.name FROM student AS S
WHERE S.sid = E.sid) AS sname
FROM enrolled as E
WHERE cid='15-455';
SELECT should be followed by an output, but why here there is another SELECT? How to understand the step-by-step meaning of this query?
The following is the query that has the same result of the above query, but its meaning is rather explicit: the output of the second SELECT is passed into the IN() function.
SELECT name FROM student
WHERE sid IN (
SELECT sid FROM enrolled
WHERE cid = '15-445'
);
Here are the original tables of this question:
mysql> select * from student;
+-------+--------+------------+------+---------+
| sid | name | login | age | gpa |
+-------+--------+------------+------+---------+
| 53666 | Kanye | kayne#cs | 39 | 4.00000 |
| 53688 | Bieber | jbieber#cs | 22 | 3.90000 |
| 53655 | Tupac | shakur#cs | 26 | 3.50000 |
+-------+--------+------------+------+---------+
mysql> select * from enrolled;
+-------+--------+-------+
| sid | cid | grade |
+-------+--------+-------+
| 53666 | 15-445 | C |
| 53688 | 15-721 | A |
| 53688 | 15-826 | B |
| 53655 | 15-445 | B |
| 53666 | 15-721 | C |
+-------+--------+-------+
mysql> select * from course;
+--------+------------------------------+
| cid | name |
+--------+------------------------------+
| 15-445 | Database Systems |
| 15-721 | Advanced Database Systems |
| 15-826 | Data Mining |
| 15-823 | Advanced Topics in Databases |
+--------+------------------------------+
In real life I'd say both queries are just two creepy ways to avoid joins.
But in this particular case they were included in the slides you've found in order to show in how many place nested loops can be used.
They all do the same thing as the following
SELECT name
FROM student s
JOIN enrolled e
ON s.sid = e.sid
WHERE cid = '15-445';
As for your question about step-by-step meaning of the first query. It is the following
This will loop through every record from "enrolled" table that has cid = '15-455'.
FROM enrolled as E
WHERE cid='15-455';
For every record from step 1 it will perform the following query
SELECT S.name
FROM student AS S
WHERE S.sid = E.sid;
This construct:
SELECT (SELECT S.name FROM student S WHERE S.sid = E.sid) AS sname
-------^
is called a scalar subquery. This is a special type of subquery that has two important properties:
It returns one column.
It returns at most one row.
In this case, the scalar subquery is also a correlated subquery meaning that it references columns in the outer query, via the where clause.
A scalar subquery can be using almost anywhere that a scalar (i.e. constant value) can be used in a query. They can be handy. They are not exactly equivalent to a join, because:
An inner join can filter values. A scalar subquery returns NULL if there are no rows returned.
A join can multiply the number of rows. A scalar subquery returns an error if it returns more than one row.
If you want to get informations like :
Name of student | CID | Grade |
You can do something like :
select t.name, e.cid, e.grade
from enrolled e
inner join student t on (e.sid = t.sid)
Or without join (for optimization) :
select (name from student t where t.sid = e.sid) as name, e.cid, e.grade
from enrolled e
so results are the same but in the second one you're avoiding joins.

SQL Join to the latest record in MS ACCESS

I want to join tables in MS Access in such a way that it fetches only the latest record from one of the tables. I've looked at the other solutions available on the site, but discovered that they only work for other versions of SQL. Here is a simplified version of my data:
PatientInfo Table:
+-----+------+
| ID | Name |
+-----+------+
| 1 | John |
| 2 | Tom |
| 3 | Anna |
+-----+------+
Appointments Table
+----+-----------+
| ID | Date |
+----+-----------+
| 1 | 5/5/2001 |
| 1 | 10/5/2012 |
| 1 | 4/20/2018 |
| 2 | 4/5/1999 |
| 2 | 8/8/2010 |
| 2 | 4/9/1982 |
| 3 | 7/3/1997 |
| 3 | 6/4/2015 |
| 3 | 3/4/2017 |
+----+-----------+
And here is a simplified version of the results that I need after the join:
+----+------+------------+
| ID | Name | Date |
+----+------+------------+
| 1 | John | 4/20/2018 |
| 2 | Tom | 8/8/2010 |
| 3 | Anna | 3/4/2017 |
+----+------+------------+
Thanks in advance for reading and for your help.
You can use aggregation and JOIN:
select pi.id, pi.name, max(a.date)
from appointments as a inner join
patientinfo as pi
on a.id = pi.id
group by pi.id, pi.name;
something like this:
select P.ID, P.name, max(A.Date) as Dt
from PatientInfo P inner join Appointments A
on P.ID=A.ID
group by P.ID, P.name
Both Bing and Gordon's answers work if your summary table only needs one field (the Max(Date)) but gets more tricky if you also want to report other fields from the joined table, since you would need to include them either as an aggregated field or group by them as well.
Eg if you want your summary to also include the assessment they were given at their last appointment, GROUP BY is not the way to go.
A more versatile structure may be something like
SELECT Patient.ID, Patient.Name, Appointment.Date, Appointment.Assessment
FROM Patient INNER JOIN Appointment ON Patient.ID=Appointment.ID
WHERE Appointment.Date = (SELECT Max(Appointment.Date) FROM Appointment WHERE Appointment.ID = Patient.ID)
;
As an aside, you may want to think whether you should use a field named 'ID' to refer to the ID of another table (in this case, the Apppintment.ID field refers to the Patient.ID). You may make your db more readable if you leave the 'ID' field as an identifier specific to that table and refer to that field in other tables as OtherTableID or similar, ie PatientID in this case. Or go all the way and include the name of the actual table in its own ID field.
Edited after comment:
Not quite sure why it would crash. I just ran an equivalent query on 2 tables I have which are about 10,000 records each and it was pretty instanteneous. Are your ID fields (i) unique numbers and (ii) indexed?
Another structure which should do the same thing (adapted for your field names and assuming that there is an ID field in Appointments which is unique) would be something like:
SELECT PatientInfo.UID, PatientInfo.Name, Appointments.StartDateTime, Appointments.Assessment
FROM PatientInfo INNER JOIN Appointments ON PatientInfo_UID = Appointments.PatientFID
WHERE Appointments.ID = (SELECT TOP 1 ID FROM Appointments WHERE Appointments.PatientFID = PatientInfo_UID ORDER BY StartDateTime DESC)
;
But that is starting to look a bit contrived. On my data they both produce the same result (as they should!) and are both almost instantaneous.
Always difficult to troubleshoot Access when it crashes - I guess you see no error codes or similar? Is this against a native .accdb database or another server?

*=* join? Is there such a thing? [duplicate]

This question already has answers here:
SQL JOIN and different types of JOINs
(6 answers)
Closed 3 years ago.
I need run a query that is like an left/right outer join. In other words I need all rows from both the left and right tables. But I don't need a cartesian product (cross join). I need to match on, in my case, email address. So given that, I have to output all rows from the left table, join the right table on email address, but all rows that do not match from either the left or right table need to be output as well with nulls for the fields from the opposite table. Sort of like a = join if there were such a thing, or left-righ outer join.
As for what I've tried: Google Searches. But didn't find anything. Cross apply might work, but I cannot wrap my brain around how that is any different from a join.
Example theoretical left-right join:
select users.*, contacts.*
from users
left-right join contacts on users.emailAddress = contacts.emailAddress
So if users contains:
----------------------------------
|emailAddress | firstName |
----------------------------------
|k#company.com | ken |
|b#enterprise.com | bill |
|j#establishment.com | joe |
----------------------------------
And contacts contains:
--------------------------------
|emailAddress | optedOut |
--------------------------------
|z#bigcompany.com | 0 |
|b#enterprise.com | 1 |
|h#smallcompany.com | 1 |
--------------------------------
The output should look like:
------------------------------------------------------------------
|emailAddress | firstName |emailAddress | optedOut |
------------------------------------------------------------------
|k#company.com | ken | NULL | NULL |
|b#enterprise.com | bill | b#enterprise.com | 1 |
|j#establishment.com | joe | NULL | NULL |
|NULL | NULL | z#bigcompany.com | 0 |
|NULL | NULL | h#smallcompany.com | 1 |
------------------------------------------------------------------
It's called a "full outer join". Your query should look like:
select users.*, contacts.*
from users
full outer join contacts on users.emailAddress = contacts.emailAddress

Search by date in array of objects within PostgreSQL JSONB Column

I have two tables in my PostgreSQL 9.6 instance.
users
+----+------------+-----------+-------------------+
| id | first_name | last_name | email |
+----+------------+-----------+-------------------+
| 1 | John | Doe | john.doe#test.com |
+----+------------+-----------+-------------------+
| 2 | Jane | Doe | jane.doe#test.com |
+----+------------+-----------+-------------------+
| 3 | Mike | Doe | mike.doe#test.com |
+----+------------+-----------+-------------------+
surveys
+----+---------+----------------------------------------------------------------------------------------------------+
| id | user_id | survey_data |
+----+---------+----------------------------------------------------------------------------------------------------+
| 1 | 1 | {'child_list': [{'gender': 1, 'birthday': '2015-10-01'}, {'gender': 2, 'birthday': '2017-05-01'}]} |
+----+---------+----------------------------------------------------------------------------------------------------+
| 2 | 2 | {'child_list': []} |
+----+---------+----------------------------------------------------------------------------------------------------+
| 3 | 3 | {'child_list': [{'gender': 2, 'birthday': '2008-01-01'}]} |
+----+---------+----------------------------------------------------------------------------------------------------+
I would like be able to query these two tables to get the number of users who have children between certain age. The survey_data column in surveys table is a JSONB column.
So far I've tried using jsonb_populate_recordset with LATERAL joins. I was able to SELECT the child_list array as two columns but couldn't figure out how to use that with my JOIN between users and surveys tables. The query I used is as below:
SELECT DISTINCT u.email
FROM surveys
CROSS JOIN LATERAL (
SELECT *
FROM jsonb_populate_recordset(null::json_type, (survey.survey_data->>'child_list')::jsonb) AS d
) d
INNER JOIN users u ON u.id = survey.user_id
WHERE d.birthday BETWEEN '2014-05-05' AND '2018-05-05';
This also uses a custom type which was created using this:
CREATE type json_type AS (gender int, birthday date)
My question is, is there an easier to read way to do this? I would like to use this query with many other JOINs and WHERE clauses and I was wondering if there is a better way of doing this.
Note: this is mainly going to be used by a reporting system which does not need to be super fast but of course any speed gains are welcome.
Use the function jsonb_array_elements(), examples:
select email, (elem->>'gender')::int as gender, (elem->>'birthday')::date as birthday
from users u
left join surveys s on s.user_id = u.id
cross join jsonb_array_elements(survey_data->'child_list') as arr(elem)
email | gender | birthday
-------------------+--------+------------
john.doe#test.com | 1 | 2015-10-01
john.doe#test.com | 2 | 2017-05-01
mike.doe#test.com | 2 | 2008-01-01
(3 rows)
or
select distinct email
from users u
left join surveys s on s.user_id = u.id
cross join jsonb_array_elements(survey_data->'child_list') as arr(elem)
where (elem->>'birthday')::date between '2014-05-05' and '2018-05-05';
email
-------------------
john.doe#test.com
(1 row)
You can make your life easier using a view:
create view users_children as
select email, (elem->>'gender')::int as gender, (elem->>'birthday')::date as birthday
from users u
left join surveys s on s.user_id = u.id
cross join jsonb_array_elements(survey_data->'child_list') as arr(elem);
select distinct email
from users_children
where birthday between '2014-05-05' and '2018-05-05';

Issue with JOIN command

Hi New comer to Stack overflow so if I do not present this correctly I am sorry.
I have used Google, W3schools and read the FQA on SQL.
I am running SQL using the SQL command line in WAMP2.0. I am currently doing a project where the aim is to create a min University DB. With students, grades, programmes, modules ect
One of the tasks is to to list all the students, there modules and there correspoding grades. To do this I am trying to use a JOIN command to select all the names from the Students table, with all there corresponding modules + grades from the records table.
+------------+-------+------------+-----------------+
| Student_id | Name | DOB | Address |
+------------+-------+------------+-----------------+
| 4665236 | Paddy | 1985-09-18 | 123 Fake Street |
| 5665236 | Paul | 1984-06-12 | Good manlane |
| 6665236 | John | 1984-03-09 | Docotor town |
| 7665236 | Aidan | 1983-07-09 | Banker worlds |
| 8665236 | Joe | 1983-07-09 | 24 hitherwood |
+------------+-------+------------+-----------------+
+------------+--------+------+-------+
| Student_id | Mod_id | GPA | Grade |
+------------+--------+------+-------+
| 4655236 | 2222 | 3.84 | A- |
| 5655236 | 11111 | 3.44 | B+ |
| 6655236 | 33333 | 3.24 | B |
| 7655236 | 44444 | 2.45 | C- |
| 8655236 | 44444 | 2.45 | C- |
+------------+--------+------+-------+
The PRIMARY KEY in the students table is Student_id INT 11
The PRIMARY KEY for records is (Student_id,Mod_id)
Individual SELECT FROM , statements work fine on both tables.
Issue occurs when I use
SELECT students.Name, records.Grade
FROM students
INNER JOIN records
ON students.Student_id=Student_id
ORDER BY students.Name
I get the following error
ERROR 1052 (23000): Column 'Student_id' in on clause is ambiguous
Thanks for amazingly fast response I tried
SELECT students.Name, records.Grade
FROM students
INNER JOIN records
ON students.Student_id=records.Student_id
ORDER BY students.Name;
And Got ---- Empty set (0.00 sec) ?
You have to qualify that column Student_Id with an alias, something like records.studentId so that it will be un ambiguous in the ON clause, or:
SELECT s.Name, r.Grade
FROM students AS s
INNER JOIN records AS r ON s.Student_id= r.Student_id
ORDER BY s.Name
You need to supply the table name for column Student_id to avoid ambiguity because it both exist on the two tables.
SELECT students.Name, records.Grade
FROM students
INNER JOIN records
ON students.Student_id = records.Student_id -- << THIS
ORDER BY students.Name
The reason you're column name is being flagged as ambiguous is because you've got two different tables that each have the Student_id field. You can join a table to itself, so even though you've got an identifier on the first instance of the field you need one on both.
Try the following code:
SELECT students.Name, records.Grade
FROM students
INNER JOIN records
ON students.Student_id=records.Student_id
ORDER BY students.Name
You can also alias the tables if that helps your code look cleaner by using the following:
SELECT s.Name, r.Grade
FROM students s
INNER JOIN records r
ON s.Student_id=r.Student_id
ORDER BY s.Name
However, this only works if the Student IDs match in both tables. in the example data you've presented there are no matching records. 4665236 != 4655236