cannot implement this inner join in oracle - sql

I have recently started to learn oracle, and I am having difficulty understanding this inner join on the tables.
INSERT INTO temp_bill_pay_ft
SELECT DISTINCT
ft.ft_id,
ft.ft_credit_acct_no,
ft.ft_debit_acct_no,
ft.ft_stmt_nos,
ft.ft_debit_their_ref,
ft.ft_date_time
FROM
funds_transfer_his ft
INNER JOIN temp_bill_pay_lwday_pl dt
ON ft.ft_id = dt.ac_ste_trans_reference || ';1'
AND ft.ft_credit_acct_no = dt.ac_id;
It is this line specifically which I dont understand, why do we use || here, I suppose it is for concatenation.
ON ft.ft_id = dt.ac_ste_trans_reference||';1'
Can somebody please explain to me this sql query. I would really appreciate it. Thank you.

This is string concatenation. The need is because there is a design error in the database and join keys are not the same in the two tables. So the data might look something like this:
ft_id ac_ste_trans_reference
123;1 123
abc;1 abc
In order for the join to work, the keys need to match. One possibility is to remove the last two characters from ft_id, but I'm guessing those are meaningful.
I can speculate on why this is so. One possibility is that ft_id is really a compound key combined into a single column -- and the 1 is used to indicate the "type" of key. If so, then there are possibly other values after this:
ft_id
123;1
garbled;2
special;3
The "2" and "3" would refer to other reference tables.
If this is the situation, then it would be cleaner to have a separate column with the correct ac_ste_trans_reference. However that occupies additional space, and can require multiple additional columns for each type. So hacks like the one you see are sometimes implemented.

Yes it is used for concatenation.
But only somebody having worked on this database model can explain what table data represent and why this concatenation is needed for this joining condition.

Related

SQL Server More Efficient Substring

I am running a query in SQL Server where I need to join two tables into one where the full name field matches the partial name field in another after apostrophes have been removed. For a code example the join is happening like this:
from [Data1]
right join [Data2]
on replace([Data2].[PartialName], '''','')=Substring([Data1].[FullName],1,1+LEN(replace([Data2].[PartialName], '''','')))
And it works. But it takes what would be a 10 second execution if we just used where name=name and makes it take around 20 minutes. This is rather unacceptable in terms of run time so I was wondering if anyone had any more efficient alternatives to consider.
Btw Data 1 has about 800 lines and Data2 has about 1.6 million if it's relevant.
Edit: I've been told I need to give a bit more descriptive information. Basically in this example Data1 is a table from an outside source that contains a name field [FullName] which contains people's full names in the form of 'Last-Name , First-Name Middle-Name(s)' with any apostrophes removed (for example in the name O'Neil it would just be ONeil).
So an example would be 'ONeil , Sarah Conner'
Data2 contains a name field that has names in the form 'Last-Name , First-Name' Middle names are omitted and apostrophes are intact. So for example 'O'Neil , Sarah'
These tables need to be merged together on their name fields, hence the logic above.
DavidG is right, a PERSISTED column is the way to go here. After drinking a little more coffee, I think you need a computed column and then LIKE in your JOIN. The PERSISTED column's SQL would be something like:
ALTER TABLE [Data2] ADD PartialName_na AS REPLACE(PartialName,'''','') PERSISTED;
You may want to add that to an index. Then your new (pseudo) SQL query would be:
SELECT ...
FROM Data2 D2
LEFT JOIN Data1 D1 ON D1.FullName = D2.PartialName_na + '%';
There's no need to use SUBSTRING. LIKE will maintain SARGability here, it doesn't use a leading wildcard.
Edit: Couple of notes. I used the _na suffix to stand for "No Apostrophe"; you can call the column whatever you want. I also changed the query from a RIGHT JOIN to a LEFT JOIN. Personally I feel that LEFT JOINs are much easier to read, however, if you want to swap it back round, feel free.

Difference between SQL Server codes?

For a school assignment, I have to create a database and run reports. I've created code and a classmate has also created code and it runs the same thing, but his is in a format I've not seen and don't quite understand.
Here is mine:
SELECT
Course.Name AS 'Course Name',
Program.Name AS 'Program Name'
FROM
Course, Program, ProgramCourse
WHERE
ProgramCourse.CourseID = Course.ID
AND
ProgramCourse.ProgramID = Program.ID
GO
And here's his:
CREATE VIEW NumberOfCoursePerProgram AS
SELECT
p.name AS ProgramName,
c.name AS CourseName
FROM
Program p
JOIN
ProgramCourse pc ON pc.ProgramID = p.ID
JOIN
Course c ON c.ID = pc.CourseID
GO
I ran both queries using the data in the tables I've created. They return practically the same results, just in a slightly different order but it fulfills the assignment question. Anyway, if I delete the p from Program p from his code, it returns an error
The multi-part identifier "p.name" could not be bound.
So how is SQL Server able to accept p.name and p.ID, etc. when I haven't ever established these variables? I don't quite understand how the code is working on his. Mine seems simple and straightforward, and I definitely understand what's going on there. So can someone explain his?
Thanks
There's a few differences. First off, he's creating a VIEW rather than just a select statement:
CREATE VIEW NumberOfCoursePerProgram AS
Once the view is created, you can query the view just as you would a table:
SELECT * FROM NumberOfCoursePerProgram;
Second, he's using an ANSI JOIN rather than an implicit JOIN. His method is more modern and most likely considered more correct by today's standards:
JOIN ProgramCourse pc ON pc.ProgramID = p.ID
JOIN Course c ON c.ID= pc.CourseID
Rather than:
FROM Course, Program, ProgramCourse
Also, note he's assigning table aliases when he refers to a table:
FROM Program p
The p at the end allows you to substitute p rather than specify the entire table name of Program elsewhere in the query. For example, you can now say WHERE p.Foo > 5 rather than WHERE Program.Foo > 5. In this case, it's just a shortcut and saves a few characters. However, suppose you were referring to the same table twice (for example, JOINing in two different rows on the same table). In that case, you might have to provide aliases for each table to disambiguate which one is which.
These are called alias in SQL. Alias is basically created to give more readability and for better ease of writing code.
The readability of a SELECT statement can be improved by giving a
table an alias, also known as a correlation name or range variable. A
table alias can be assigned either with or without the AS keyword:
table_name AS table alias
table_name table_alias
So in your query p is an alias to Program so that means now you can refer your table Program by the name of p instead of writing the whole name Program everywhere.
Similarly you can access the names of the columns of your table Program by simply writing p with a dot and then the column name. Something like p.column. This technique is very useful when you using JOINS and some your tables have similar names of the columns.
EDIT:-
Although most of the points are covered in other's answer. I am just adding a point that you should avoid the habit of JOINING table the way you are doing it right now.
You may check Bad habits to kick : using old-style JOINs by Aaron Bertrand for reference.
CREATE VIEW NumberOfCoursePerProgram AS
BEGIN
SELECT
p.name AS ProgramName,
c.name AS CourseName
FROM
Program p
JOIN
ProgramCourse pc ON pc.ProgramID= p.ID
JOIN Course c ON c.ID= pc.CourseID
END
GO
Observe that both tables Program and Course have a table alias defined.
The select part must specify the table from which the column name comes from. Which is exactly what you did. Your partner just added aliases to the tables names. These aliases are shorter, and makes the query look a bit less like a big wall of text.
The other difference is the use of joints. The joins are usually used to link results from two tables that has a corresponding column.
The columns are usually the primary key, and the foreign key in the second table.
Your query is fine, but the join syntax is preferred.
Edit : Once the view is created, it is now compiled and can be used in a select just like a table.
SELECT * FROM NumberOfCoursePerProgram
If you need to modify the view, you can use
ALTER VIEW NumberOfCoursePerProgram AS
......
......
There are a few things going on here:
if I delete the "p" from "Program p" from his code, it returns "The multi-part identifier "p.name" could not be bound."
The P is an alias for program. By doing it that way. If you ever need to use Program you can just call it P.
As far as what he is doing: it's better to do a JOIN in this case as opposed to doing a multi-table SELECT that you are doing. In simple cases it doesn't really matter but what if course and program both had millions of rows. In your way you are basically selecting everything. By doing the join on ProgramID you are only getting those items in ProgramCourse that correspond to an entry in Program as well (tied together by ID and CourseID).
One more important note. You are doing a simple SELECT statement. In SQL there are objects called VIEWS that act as a virtual table. He can now do any time a SELECT * FROM NumberOfCoursePerProgram and he will never have to do any of the joins, and selects again.
Hope that helps...
In SQL Server you can give a table an alias without AS, its optional.
If you were to add the AS it would will work just like you have it.
It may also be worthy to note that he is using a join as opposed to a where clause join, which is my preference because it's easier to read and more update code ethic.
Sometimes you need to join on a WHERE clause because of multiple conditions on join, but that's pretty rare in your case.

difficulty with an sql query using Oracle database

the question:
Find the title of the books whose keyword contains the last 3 characters of the bookgroup of the book which was booked by "Mr. Karim".
I am trying to do it like this:
SELECT Title
FROM Lib_Book
WHERE BookKeywords LIKE '%(SELECT BookGroup FROM Lib_Book WHERE BookId=(SELECT BookId from Lib_Booking, Lib_Borrower WHERE Lib_Booking.BId=Lib_Borrower.BId AND Lib_Borrower.BName = 'Mr. Karim'))%';
from the part after % upto the end returns me an answer which is 'programming'. so i need to indicate the BookKeyword as '%ing%'. How can i do that?
**the tables are huge so i hvnt written those here..if anyone need to those plz lemme know...thnx
You have the basic concept down, although it's not possible to process a SELECT statement inside a LIKE clause (and I'd probably shoot the RDBMS developer who allowed that - it's a GAPING HUGE HOLE for SQL Injection). Also, you're likely to have problems with multiple results, as your 'query' would fail the moment Mr. Karim had borrowed more than one book.
I'd probably start by attempting to make things run off of joins (oh, and never use implicit join syntax):
SELECT d.title
FROM Lib_Borrower as a
JOIN Lib_Booking as b
ON b.bId = a.bId
JOIN Lib_Book as c
ON c.bookId = b.bookId
JOIN Lib_Book as d
ON d.bookKeywords LIKE '%' + SUBSTRING(c.bookGroup, LENGTH(c.bookGroup) - 3) + '%'
WHERE a.bName = 'Mr. Karim'
Please note the following caveats:
This will get you all titles for all books with similar keywords to all borrowed books (from all 'Mr. Karim's). You may need to include some sort of restrictive criteria while joining to Lib_Booking.
The column bookKeywords seems potentially like a multi-value column (would need example data). If so, your table structure needs to be revised.
The use of SUBSTRING() or RIGHT() will invalidate the use of inidicies in joining to the bookGroup column. There isn't much you can necessarily do about that, given your requirements...
This table is not internationalization safe (because the bookGroup column is language-dependant parsed text). You may find yourself better served by creating a Book_Group table, a Keywords table, and a cross-reference Book_Keywords table, and joining on numerical ids. You may also want language-keyed Book_Group_Description and Keyword_Description tables. This will take more space, and probably take more processing time (increased number of joins, although potentially less textual processing), but give you increased flexibility and 'safety'.

Is there a way to rename a similarly named column from two tables when performing a join?

I have two tables that I am joining with the following query...
select *
from Partners p
inner join OrganizationMembers om on p.ParID = om.OrganizationId
where om.EmailAddress = 'my_email#address.com'
and om.deleted = 0
Which works great but some of the columns from Partners I want to be replaced with similarly named columns from OrganizationMembers. The number of columns I want to replace in the joined table are very few, shouldn't be more than 3.
It is possible to get the result I want by selectively choosing the columns I want in the resulting join like so...
select om.MemberID,
p.ParID,
p.Levelz,
p.encryptedSecureToken,
p.PartnerGroupName,
om.EmailAddress,
om.FirstName,
om.LastName
from Partners p
inner join OrganizationMembers om on p.ParID = om.OrganizationId
where om.EmailAddress = 'my_email#address.com'
and om.deleted = 0
But this creates a very long sequence of select p.a, p.b, p.c, p.d, ... etc ... which I am trying to avoid.
In summary I am trying to get several columns from the Partners table and up to 3 columns from the OrganizationMembers table without having a long column specification sequence at the beginning of the query. Is it possible or am I just dreaming?
select om.MemberID as mem
Use th AS keyword. This is called aliasing.
You are dreaming in your implementation.
Also, as a best practice, select * is something that is typically frowned upon by DBA's.
If you want to limit the results or change anything you must explicitly name the results, as a potential "stop gap you could do something like this.
SELECT p.*, om.MemberId, etc..
But this ONLY works if you want ALL columns from the first table, and then selected items.
Try this:
p.*,
om.EmailAddress,
om.FirstName,
om.LastName
You should never use * though. Always specifying the columns you actually need makes it easier to find out what happens.
But this creates a very long sequence
of select p.a, p.b, p.c, p.d, ... etc
... which I am trying to avoid.
Don't avoid it. Embrace it!
There are lots of reasons why it's best practice to explicity list the desired columns.
It's easier to do searches for where a particular column is being used.
The behavior of the query is more obvious to someone who is trying to maintain it.
Adding a column to the table won't automatically change the behavior of your query.
Removing a column from the table will break your query earlier, making bugs appear closer to the source, and easier to find and fix.
And anything that uses the query is going to have to list all the columns anyway, so there's no point being lazy about it!

Should I Always Fully Qualify Column Names In SQL?

Out of interest when working with SQL statements should I always use the fully qualifed column name (tablename.columnname) even if only working with one table e.g.
SELECT table.column1, table.column2 FROM table
It's better if you do - it doesn't add any complexity, and it can prevent errors in the future.
But in a well-defined system, you shouldn't have to - it's like namespaces in programming languages. The ideal is not to have conflicts, but it can clutter the code with the superfluous use of explicit names.
-Adam
I generally follow these rules:
When using a single table, it is not necessary to use the table name prefix:
SELECT col1, col2 FROM table1
For multiple tables, use the full table name. Aliases can be confusing, especially when doing multiple joins:
SELECT table1.col1, table2.col2 FROM table1 INNER JOIN table2 on
table1.id = table2.id
I see many developers using table aliases, but particularly in large projects with multiple developers, these can become cryptic. A few extra keystrokes can provide a lot more clarity in the code.
If may, however, become necessary to use a column alias when columns have the same name. In that case:
SELECT table1.col1, table2.col1 as table2_col1 FROM table1
INNER JOIN table2 on
table1.id = table2.id
I would put this as personal preference. It would only make a difference if you started joining tables which contain duplicate column names.
Also, rather than write out the table name in full, use an alias:
SELECT t.column1, t.column2 FROM table as t
If you are only selecting from one table I do not see the overall usefulness. If you are selecting from multiple tables, qualifying the column names would certainly make it easier to read for any other developer who may not be familiar with your database schema.
If you're only querying one table - I'd say no. It's more readable that way.
Don't solve problems you don't have yet. (At least that's what my team lead is always telling me.) I'm sure the monkey who someday has to add a JOIN to your statement can figure it out.
I think it is a good idea to always use the fully qualified column name. Use an alias if the table name is too long. It also prepares your queries for futures additions of e.g. joins.
I would say it is nice to use qualified name, it adds readability to your code. It does not make much sense to use it for single table but for multiple tables it is must. if table names are too big then it is recommended to use alias, alias should preferably be derived from table name.
SELECT Dep.Name,Emp.Name
FROM Department DEP INNER JOIN Employee Emp
ON Dep.departmentid=Emp.DepartmentID
No.
You should always alias the tables, and you should always qualify your column names with the table aliases.
select
p.FirstName,
p.LastName,
p.SSN
from Person p
where p.ID = 345
I prefer to use a table alias when more than 1 table is in play.
Even the most well defined systems are subject to change. A new field could quite easily be introduced to a table causing ambiguity in an existing query.
Fully qualifying them protects you from this scenario.