I need programmatically edit SQL commands in such way that each table in the SQL command will have an alias. The input is a schema for the database and an SQL command. The output should be an SQL query where every table and has an alias and it is always used when we reference an attribute of that table.
For example, let us have a database person(id, name, salary, did) and department(did, name) and the following SQL command:
select id, t.did, maxs
from person
join (
select did, max(salary) maxs
from person
group by did
) t on t.maxs = salary and person.did = t.did
The expected result for such input would be
select p1.id, t.did, t.maxs
from person p1
join (
select p2.did, max(p2.salary) maxs
from person p2
group by p2.did
) t on t.maxs = p1.salary and p1.did = t.did
I was considering using ANTLR4 for this, however, I was curious whether there is a simpler solution. I recently come across TSqlParser, is it possible to use this class to achieve such rewrite in some simple way?
Related
I am trying to display employee properties using C# WPF view.
I have data in '2' different oracle tables in my database:
Those tables structure at high-level is...
Employee table (EMP) - columns:
ID, Name, Organisation
Employee properties table (EMPPR) - columns
ID, PropertyName, PropertyValue
The user will input 'List of Employee Name' and I need to display Employee properties using data in those '2' tables.
Each employee has properties from 40-80 i.e. 40-80 rows per employee in EMPPR table. In this case, which approach is more efficient?
Approach #1 - single query data retrieval:
SELECT Pr.PropertyName, Pr.PropertyValue
FROM EMP Emp, EMPPR Pr
WHERE Emp.ID = Pr.ID
AND Emp.Name IN (<List of Names entered>)
Approach #2 - get IDs list using one query and Get properties using that ID in the second query
Query #1:
SELECT ID
FROM EMP
WHERE Name IN (<List of Names entered>)
Query #2:
SELECT PropertyName, PropertyValue
FROM EMPPR
WHERE ID IN (<List of IDs got from Query#1>)
I need to retrieve ~10K employee details at once where each employee has 40-80 properties.
Which approach is good?
Which query is faster?
The first one, which uses a single query to fetch your results.
Why? much of the elapsed time handling queries, especially ones with modestly sized rows like yours, is consumed going back and forth from the client to the database server.
Plus, the construct WHERE something IN (val, val, val, val ... ... val) can throw an error when you have too many values. So the first query is more robust.
Pro tip: Come on into the 21st century and use the new JOIN syntax.
SELECT Pr.PropertyName, Pr.PropertyValue
FROM EMP Emp
JOIN EMPPR Pr ON Emp.ID = Pr.ID
WHERE Emp.Name IN (<List of Names Inputted>)
Use first approach of join between two tables which is far better than using where clause two times.
I want to create a view where all birthdays of all persons are listed. Lets say I want to "expand" a table.
I have a table Person with the attributes ID and Birthday.
I have a function FuncBirthdays that takes an ID and a Birthday and then it lists all birthdays until the current date. For example:
SELECT * FROM FuncBirthdays(123, '01.01.2014');
Result:
123; '01.01.2014'
123; '01.01.2015'
123; '01.01.2016'
// Current date is 14.01.2016, so the list stops here.
Now I want to create a view with all birthdays of all persons in table Person.
SELECT *
FROM
Person
INNER JOIN
(SELECT * FROM FuncBirthdays(Person.ID, Person.Birthday)) FB
ON Person.ID = FB.ID
The error message in the MS SQL Server 2012 Studio is that Person.ID and Person.Birthday are unbound.
I'm guessing that this way collides with the way JOINS are done. What SQL-concepts could I use to expand a table? Every data set in Person generates a variable amount of data sets in the output. Currently I have solved this problem with a C#.net function that collects the data manually. Is there a pure SQL way?
Thanks
Martin
"The error message in the MS SQL Server 2012 Studio is that Person.ID and Person.Birthday are unbound."
Is because of you are trying to access person in Join result set which can not be accessible. You need to use cross apply in that case, as per my knowledge. Please try following query
SELECT * FROM Person p
CROSS APPLY
(
SELECT * FROM FuncBirthdays(p.ID, p.Birthday)
) FB
WHERE p.ID = FB.ID
With CTE1 (ID, Birthday) AS
(
SELECT * FROM FuncBirthdays(p.ID, p.Birthday)
)
SELECT *
FROM CTE1, Person p
WHERE p.ID = CTE1.ID
I am using the following query:
select P.job_ref, P.emp_num, P.name,
P.job_title , P.job_type,
P.dept_ref, P.dept, J.mgr_rept,
(select P.name as manager_name
from PEOPLE P, JOB J
where J.mgr_rept=P.job_ref)
from PEOPLE P, JOB J
where P.job_ref=J.job_ref
The issue is the manager and employee names reside in the same table under name. job_ref correlates to mgr_rept in the job table. An employees job_ref would equal a value in the job table that would pull up job information. The manager name however lies in the people table. The mgr_rept value would be the managers job_ref number. Using this query returns more than one row so I'm looking for some help please.
Thank you
First of all, you should no longer use the old comma-separated join syntax. It has been replaced in standard SQL with explicit joins more than 20 years ago.
Your subquery selects all people. It is not linked anyway to the record in the main query. Maybe you confused yourself by using the same table aliases again. It should be about this instead:
select
P.job_ref, P.emp_num, P.name,
P.job_title , P.job_type,
P.dept_ref, P.dept, J.mgr_rept,
(
select mgr.name
from PEOPLE mgr
where mgr.job_ref = J.mgr_rept
) as manager_name
from PEOPLE P
join JOB J on P.job_ref = J.job_ref;
I'm not 100% sure however, because I don't understand your table structure completely. This only works if job_ref is unique in table people.
I am trying to use a second SELECT to get some ID, then use that ID in a second SELECT and I have no idea how.
SELECT Employee.Name
FROM Emplyee, Employment
WHERE x = Employment.DistributionID
(SELECT Distribution.DistributionID FROM Distribution
WHERE Distribution.Location = 'California') AS x
This post got long, but here is a short "tip"
While the syntax of my select is bad, the logic is not. I need that "x" somehow. Thus the second select is the most important. Then I have to use that "x" within the first select. I just don't know how
/Tip
This is the only thing I could imagine, I'm very new at Sql, I think I need a book before practicing, but now that I've started I'd like to finish my small program.
EDIT:
Ok I looked up joins, still don't get it
SELECT Employee.Name
FROM Emplyee, Employment
WHERE x = Employment.DistributionID
LEFT JOIN Distribution ON
(SELECT Distribution.DistributionID FROM Distribution
WHERE Distribution.Location = 'California') AS x
Get error msg at AS and Left
I use name to find ID from upper red, I use the ID I find FROM upper red in lower table. Then I match the ID I find with Green. I use Green ID to find corresponding Name
I have California as output data from C#. I want to use California to find the DistributionID. I use the DistributionID to find the EmployeeID. I use EmployeeID to find Name
My logic:
Parameter: Distribution.Name (from C#)
Find DistributionID that has Distribution.Name
Look in Employment WHERE given DistributionID
reveals Employees that I am looking for (BY ID)
Use that ID to find Name
return Name
Tables:
NOTE: In this example picture the Employee repeats because of the select, they are in fact singular
In "Locatie" (middle table) is Location, I get location (again) from C#, I use California as an example. I need to find the ID first and foremost!
Sory they are not in english, but here are the create tables:
Try this:
SELECT angajati.Nume
FROM angajati
JOIN angajari ON angajati.AngajatID = angajari.AngajatID
JOIN distribuire ON angajari.distribuireid = distribuire.distribuireid
WHERE distribuire.locatie = 'california'
As you have a table mapping employees to their distribution locations, you just need to join that one in the middle to create the mapping. You can use variables if you like for the WHERE clause so that you can call this as a stored procedure or whatever you need from the output of your C# code.
Try this solution:
DECLARE #pLocatie VARCHAR(40)='Alba'; -- p=parameter
SELECT a.AngajatID, a.Nume
FROM Angajati a
JOIN Angajari j ON a.AngajatID=j.AngajatID
JOIN Distribuire d ON j.DistribuireID=d.DistribuireID
WHERE d.Locatie=#pLocatie
You should add an unique key on Angajari table (Employment) thus:
ALTER TABLE Angajari
ADD CONSTRAINT IUN_Angajari_AngajatID_DistribuireID UNIQUE (AngajatUD, DistribuireID);
This will prevent duplicated (AngajatID, DistribuireID).
I don't know how you are connecting Emplyee(sic?) and Employment, but you want to use a join to connect two tables and in the join specify how the tables are related. Joins usually look best when they have aliases so you don't have to repeat the entire table name. The following query will get you all the information from both Employment and Distribution tables where the distribution location is equal to california. You can join employee to employment to get name as well.
SELECT *
FROM Employment e
JOIN Distribution d on d.DistributionID = e.DistributionID
WHERE d.Location = 'California'
This will return the contents of both tables. To select particular records use the alias.[Col_Name] separated by a comma in the select statement, like d.DistributionID to return the DistributionID from the Distribution Table
I have this query:
SELECT to_number(gr.code) group_index,
gr.NAME group_name, f.*,
gr.description gr_desc
FROM obj$groups gr, obj$group_objects gro,
obj$group_objects gro2, tdf$flex_fields f,
inv$requests w, inv$Direction_Criterions c
WHERE gr.NO = gro.object_no
AND gro.group_no = obj$group_service.path_to_no(
'Some Condition String',
tdf$flex_field_service.get_local_list_group_no)
AND gro2.group_no = gr.NO
AND f.NO = gro2.object_no
AND w.no = 11593597
AND c.direction_no = w.direction_no
AND f.no = c.criterion_no
ORDER BY to_number(gr.code), f.name
Why are two same tables (group_objects) present here? I tried to reverse-engineer this, but couldn't myself, maybe anyone here already know of this trick?
This happens in Oracle database.
It's an operation called self-join. When you want to join records from the same table.
It usually happens when you have records related to records in the same table. Example:
create table tree
(
id number primary key,
parent_id number,
value varchar2(100)
);
So, if you want to retrieve nodes and their parents you would do:
select c.id, c.value, p.value as parent_value
from tree c inner join tree p on (c.parent_id = p.id)
Something similar is happening in the query you posted.
group_objects is being joined to groups in the clause gro2.group_no = gr.NO and to flex_fields in the clause f.NO = gro2.object_no. I suspect this covers the case where obe set of group object isn't exactly the same as the other set and this limits the rows in the two joins where one join removes a group_object that would then not be available to join to the other table.
It's hard to divine the original programmer's intent from this snippet, especially without a description of what the various tables hold.
However, it appears to me as if the final result of this query is supposed to report information from two different records from the group_objects table — one in which the group no matches "Some Condition String" and the other in which the group no matches a column value from the groups table. If I had to guess, it's retrieving an operation in which an item was transferred between two groups, or substituted to be used in place of an object from a second group, or something like that.
For illustration, the equivalent with the standard EMP table would be:
select e.ename, m.ename manager_name
from emp e, emp m
where m.empno = e.mgr;
Or in more modern syntax:
select e.ename, m.ename manager_name
from emp e
join emp m on m.empno = e.mgr;
i.e. show the names of employees with managers and the name of their managers.
The point is that the same table is used twice in the query in a different "role". It need not be a self-join, there could be another table (or more) in between like this:
select e.ename, pm.ename projmanager_name
from emp e
join project_assignments pa on p.empno = pa.empno
join projects p on p.proj_id = pa.proj_id
join emp pm on pm.empno = p.projmanager_empno;
i.e. show the names of employees assigned to projects and show the name of the project manager of that project.