Oracle Statement does not count correctly - sql

I've got a SQL-statement with a - for me not explainable - strange behaviour.
Perhaps you could find what's wrong:
When I use the statement
select count(*) from department
I got 2755 results
Using the following statement
select
building1.street, building1.streetno, building1.plz, building1.city, dept1.buildingid
from
department dept1
left join
supporter sup
on
dept.supporterid = sup.id
left join
building building1
on
sup.buildingid = building1.ibuildingid
where
dept.usepostaladresssupporter = 1
union all
select
building2.street, building2.streetno, building2.plz, building2.city, dept2.buildingid
from
building building2
right join
tueks_department dept2
on
dept2.buildingid = building2.ibuildingid
where
dept2.usepostaladresssupporter = 0
I got 2755 results too.
But when I want to combine the two statements with a left join:
select count(*) from department
left join
(
select
building1.street, building1.streetno, building1.plz, building1.city, dept1.buildingid
from
department dept1
left join
supporter sup
on
dept.supporterid = sup.id
left join
building building1
on
sup.buildingid = building1.ibuildingid
where
dept.usepostaladresssupporter = 1
union all
select
building2.street, building2.streetno, building2.plz, building2.city, dept2.buildingid
from
building building2
right join
tueks_department dept2
on
dept2.buildingid = building2.ibuildingid
where
dept2.usepostaladresssupporter = 0
) postadress
on
department.buildingid = postadress.buildingid;
I got 3648513 results.
My expectation was, that I get only 2755 results.
Where's the mistake?
Thanks for help!

I assume that buildingid is not unique (for my reasoning to hold true, it can't be unique)
Imagine following simple tables
TableA
create TableA (name VARCHAR(32));
insert into TableA values ('Lieven');
insert into TableA values ('Lieven');
TableB
create TableB (name VARCHAR(32));
insert into TableB values ('Lieven');
insert into TableB values ('Lieven');
insert into TableB values ('AnyOtherValue');
Select statement
select * from TableA a left outer join TableB b on a.name = b.name
As each record of TableA is matched with each record of TableB where the name is equal, this will result in 4 records (the AnyOtherValue is dissmissed as it doesn't match)
The first record of TableA is returned with two of three records of `TableB'
The second record of TableA is returned with two of three records of `TableB'

The query
select
building1.street, building1.streetno, building1.plz, building1.city, dept1.buildingid
from
department dept1
left join
supporter sup
on
dept.supporterid = sup.id
left join
building building1
on
sup.buildingid = building1.ibuildingid
where
dept.usepostaladresssupporter = 1
union all
select
building2.street, building2.streetno, building2.plz, building2.city, dept2.buildingid
from
building building2
right join
tueks_department dept2
on
dept2.buildingid = building2.ibuildingid
where
dept2.usepostaladresssupporter = 0
will return one row per department that has usepostaladresssupporter as either 0 or 1 (note that records with other values will not be included, this may or may not be a problem depending on the constrainst of this column).
The unique key of this query results is probably something like departmentid (you will need to include that column in your select criteria).
So the correct query should look something like this:
select * from department
left join
(
select
building1.street, building1.streetno, building1.plz, building1.city, dept1.departmentid
from
department dept1
left join
supporter sup
on
dept.supporterid = sup.id
left join
building building1
on
sup.buildingid = building1.ibuildingid
where
dept.usepostaladresssupporter = 1
union all
select
building2.street, building2.streetno, building2.plz, building2.city, dept2.departmentid
from
building building2
right join
tueks_department dept2
on
dept2.buildingid = building2.ibuildingid
where
dept2.usepostaladresssupporter = 0
) postadress
on
department.departmentid = postadress.departmentid;
Your query will go wrong on data something like this:
Departmentid BuildingId Name
1 1 Dept1
2 2 Dept2
3 2 Dept3
The multiplying effect is not quite equal to deptcount * deptcount, but rather it is buildingcount * buildingcount + deptcount - buildingcount

Related

MS Access join with only the max row per item in primary table

I need to perform a join on a one to many table, but I only want to join on the row with the max value per row on my primary table.
tableA
productId
productName
EXAM-001
A test product
LAME-001
Something else
EXAM-002
Another thing
tableB
id
productId
someValue
user
1
EXAM-001
9001
Alex
2
EXAM-001
8000
Sheila
3
LAME-001
100
Alex
Desired output from query
productId
productName
topSomeValue
user
EXAM-001
A test product
9001
Alex
LAME-001
Something else
100
Alex
EXAM-002
Another thing
null
null
Example query that works in sqlite
SELECT tableA.productId, tableA.productName, tableB.someValue AS topSomeValue, tableB.user
FROM tableA
LEFT JOIN tableB
ON tableA.productId = tableB.productId
AND tableB.id = (
SELECT tableB.id
FROM tableB
WHERE tableB.productId = tableA.productId
ORDER BY tableB.someValue DESC
LIMIT 1
)
The above query works in SQLite but does not work in MS Access SQL. I have researched the problem over several days and have hit a brick wall.
What would be the equivalent in MS Access SQL?
This worked for me with the given example:
SELECT sel.productId, sel.productName, sel.TopSomeValue, TableB.user
FROM TableB
RIGHT JOIN
(SELECT TableA.productId, TableA.productName, tb_max.TopSomeValue
FROM TableA
LEFT JOIN
(SELECT productId, MAX(someValue) AS TopSomeValue
FROM TableB
GROUP BY productId) AS tb_max
ON tb_max.productId = TableA.productId) AS sel
ON (sel.TopSomeValue = TableB.someValue) AND (sel.productId = TableB.productId);

Pivot Creates an extra Row for Null values of Pivot Key

I am using oracle sql developer and trying to pivot in based on IssueId and expecting to get one row. But it creates an extra row for null values of issue but I want all values in one row. Please, see below scenario for better explanation of this confusing problem.
Table I have after joins :
Data:
Current Result with Extra Null Row:
Expected Result:
My Query:
select *
from
(
select TableB.SEQ_ID,TableA.ISSUEID , TableC.Question
from TableC RIGHT JOIN TableB ON TableC.QUESTIONID = TableB.QUESTION_ID
LEFT JOIN TableA ON TableB.QUESTION_ID = TableA.QUESTIONID AND ISSUEID = 3250
) d
pivot
(
MAX(Question)
for SEQ_ID in ( 1, 2, 3 ,4, 5 )
) piv;
Any suggestions related to this is appreciated. Thank You!
Assuming the intermediate result set is what you expect to see and the left/right outer joins are necessary for what you're trying to do, you could just replace the null issue IDs from rows not matching in table A, using an analytic aggregate:
max(TableA.ISSUEID) over () as ISSUEID
With sample data that gets the same intermediate result you showed, the modified inner query:
select TableB.SEQ_ID, max(TableA.ISSUEID) over () as ISSUEID, TableC.Question
from TableC
RIGHT JOIN TableB ON TableC.QUESTIONID = TableB.QUESTION_ID
LEFT JOIN TableA ON TableB.QUESTION_ID = TableA.QUESTIONID AND TableA.ISSUEID = 3250;
gets
SEQ_ID ISSUEID QUESTION
---------- ---------- -------------
1 3250 How are You?
2 3250 Hows it going
3 3250 Is It Okay?
4 3250 Whats Up?
5 3250 Really?
and when pivoted:
select *
from
(
select TableB.SEQ_ID, max(TableA.ISSUEID) over () as ISSUEID, TableC.Question
from TableC
RIGHT JOIN TableB ON TableC.QUESTIONID = TableB.QUESTION_ID
LEFT JOIN TableA ON TableB.QUESTION_ID = TableA.QUESTIONID AND TableA.ISSUEID = 3250
) d
pivot
(
MAX(Question)
for SEQ_ID in ( 1, 2, 3 ,4, 5 )
) piv;
gets
ISSUEID 1 2 3 4 5
---------- ------------- ------------- ------------- ------------- -------------
3250 How are You? Hows it going Is It Okay? Whats Up? Really?
You could get the same result with:
select * from (
select b.seq_id, a.issueid, c.question
from tableb b
join tablec c on c.questionid = b.question_id
cross join (
select issueid from tablea where issueid = 3250
) a
) d
pivot
(
max(question)
for seq_id in (1, 2, 3 ,4, 5)
);
(or cross apply in 12c+), which also works with condition >= 3250. This just lists all five questions against every matching issue ID. That may not actually be what you want to do, but it's what your very limited sample data and expected results suggest.
If you only want valid issues, then don't mess around with all those outer joins. Just start with the table that has the rows you want to keep, us left join for the other tables, and filter the first table in the where clause:
from (select TableB.SEQ_ID, TableA.ISSUEID , TableC.Question
from TableA LEFT JOIN
TableB
on TableB.QUESTION_ID = TableA.QUESTIONID LEFT JOIN
TableC
on TableC.QUESTIONID = TableB.QUESTION_ID
where tableA.ISSUEID = 3250
) d

SQL Multiple Joins Query

Here I have two tables committee_colleges and colleges.
Structure of tables is something like this
committee_colleges
committeeCollegeId collegeId committeeMemberId
1 2 1
2 2 2
3 3 2
I am storing committeeMemberId from committeeMember table.And one college can have multiple committee Members.How can I wite a query to display only the colleges assigned to specific committee Member.
For Example,if committeeMember by id=2 has logged in I want to display colleges by id=2,3.
In college table I have like this,
collegeId typeName
1 AICTE
2 NCTE
3 NTCS
This is Committee Member table
committeeMemberId name
1 xyz
2 abc
Now I am writing something like this,but i know its wrong because I dont know how to take it from College table since I am displaying College details.
SELECT cc.committeeCollegeId as committeeCollegeId,
c.collegeId as collegeId,
cc.committeeMemberId as committeeMemberId
FROM committee_college as cc
left outer join College as c
on cc.collegeId = c.collegeId
where cc.committeeMemberId=:committeeMemberId
order by cc.committeeCollegeId asc
Can anyone tell how to display colleges based on its assignment to particular committeeMember?
You were close, you need INNER JOIN instead of LEFT JOIN:
SELECT DISTINCT C.typeName --<<== put here all the columns that you want in output
FROM committee_colleges CC
INNER JOIN college C
ON C.collegeId = CC.collegeId
WHERE CC.committeeMemberId = 2 --<<== your input parameter
EDIT: added DISTINCT
Hope it helps.
You can use below sql statement for the same
DECLARE #committeeMemberId INT = 2 -- Id of Committee member
;WITH CTE_MemberCommittee AS
(
SELECT CollegeId
FROM committee_colleges
WHERE committeeMemberId = #committeeMemberId
)
SELECT collegeId, typeName
FROM college
WHERE collegeId IN (SELECT CollegeId FROM CTE_MemberCommittee)
You can use simple inner join for that,
If you want collegename based on memberId use following query,
select a.collegeid,a.typeName from
college a, committee_colleges b, committe_member c
where a.collegeid = b.collegeid and
b.committeememberid = c.committeeMemberId
and c.committeeMemberId = '2'
If you want collegename based on committemember name then use following query,
select a.collegeid,a.typeName from
college a, committee_colleges b, committe_member c
where a.collegeid = b.collegeid and
b.committeememberid = c.committeeMemberId
and c.Name = 'xyz'
Hope it will help.
try this:
DECLARE #LoginCommitteeMemberId INT=2
SELECT t2.Name AS MemberName,
t3.TypeName AS CollageName
FROM committee_college t1
INNER JOIN Committee_Member t2
ON t1.committeeMemberId = t2.committeeMemberId
INNER JOIN College as t3
ON t1.collegeId = t3.collegeId
WHERE t1.committeeMemberId = #LoginCommitteeMemberId

Why use left join at all

I am looking at the following link Left Join
If left join uses all elements from table 1 and doesn't care about table 2, why not just write:
SELECT table1.column_name(s)
FROM table1
instead of writing
SELECT table1.column_name(s)
FROM table1
LEFT OUTER JOIN table2
ON table1.column_name=table2.column_name;
It isn't that one cares about the values in TABLE1 and, as you write, doesn't care about TABLE2. It is that one cares about the values in TABLE1 and also about the values in TABLE2 if they exist.
Suppose you want a report of employees and their managers. You wouldn't want to exclude the president of your company simply because he doesn't have a manager. So, you would write it as a LEFT OUTER JOIN.
SELECT e.employee_name, m.employee_name manager_name
FROM employees e
LEFT JOIN employees m ON m.employee_id = e.manager_id
That way, you will get a row with your president's name and a NULL manager name for the president. If you had written it as an INNER join, you wouldn't have gotten a row for the president at all.
A LEFT JOIN on another table without a WHERE clause is indeed quite unnessairy, when only columns from the first table are needed.
In fact, it could return more rows than expected.
For example:
First some sample data:
Table1:
Id Text1 SomeKey
1 Tralala 10
2 Joehoe 20
3 Trololo 30
Table2:
Id SomeKey Text2
1 10 Blah
2 20 Bleh
3 20 Blergh
A query only on Table1 would return 3 rows.
But a query with a left join on SomeKey would return 4 rows:
select t1.Text1
from Table1 t1
left join Table2 t2 on (t1.SomeKey = t2.SomeKey)
returns:
Text1
-------
Tralala
Joehoe
Joehoe
Trololo
A common reason why one would do such a left join, without selecting fields from the joined table, is to find records in Table1 that don't have a match in Table2. Or use criteria based on Table2 fields.
select t1.Text1, t1.SomeKey
from Table1 t1
left join Table2 t2 on (t1.SomeKey = t2.SomeKey)
where t2.Id is null;
returns:
Text1 SomeKey
-------- -------
Trololo 30
"If left join uses all elements from table 1 and doesn't care about table 2"
This statement is wrong and also the sample SQLs are not even similar.
LEFT JOIN means:
All the rows from "LEFT" table, regardless there is a match on "RIGHT" or not AND all the rows from "RIGHT" where there is a match. For example think of Customers and their Orders. You might have 100 Customers and not all of them yet have Orders.
select *
from Customers c
LEFT JOIN Orders o on c.CustomerID = o.CustomerID;
would retrieve all those 100 Customer rows, where those that do not have an Order have NULL for Orders' columns and if one has N Orders, its Customer row is repeated for each of those Orders. ie:
Customers: (CustomerId, CustomerName)
1, Customer1
2, Customer2
3, Customer3
Orders:(OrderId,CustomerId,OrderDate,...)
1, 1, 2000/1/1 10:00:00, ...
2, 1, 2000/1/5 11:00:00, ...
3, 2, 2000/1/6 18:00:00, ...
Result would be:
1, Customer1, 1, 1, 2000/1/1 10:00:00, ...
1, Customer1, 2, 1, 2000/1/5 11:00:00, ...
2, Customer2, 3, 2, 2000/1/6 18:00:00, ...
3, Customer3, NULL, NULL, NULL, ...
As you can see, it doesn't select "elements" only from LEFT table, it does from both. You can specify individual or all columns from either table:
select c.CustomerID, c.CustomerName, o.OrderDate, o.OrderID
from Customers c
LEFT JOIN Orders o on c.CustomerID = o.CustomerID;
select c.*
from Customers c
LEFT JOIN Orders o on c.CustomerID = o.CustomerID;
select c.*, o.OrderId, o.OrderDate
from Customers c
LEFT JOIN Orders o on c.CustomerID = o.CustomerID;
etc.

how use distinct in second join table in sql server

I have a SQL table consists of id, name, email,.... I have another SQL table that has id, email, emailstatus but these 2 id are different they are not related. The only thing that is common between these 2 tables are emails.
I would like to join these 2 tables bring all the info from table1 and if the email address from table 1 and table 2 are same and emailstatus is 'Bounced'. But the query that I am writing gives me more record than I expected because there are multiple rows in tbl_webhook(second table) for each row in Applicant(first table) .I want to know if applicant has EVER had an email bounce.
Query without join shows 23000 record but after join shows 42000 record that is because of duplicate how I can keep same 23000 record only add info from second table?
This is my query:
SELECT
A.[Id]
,A.[Application]
,A.[Loan]
,A.[Firstname]
,A.[Lastname]
,A.[Email],
,H.[Email], H.[EmailStatus] as BouncedEmail
FROM Applicant A (NOLOCK)
left outer join [tbl_Webhook] [H] (NOLOCK)
on A.Email = H.Email
and H.[event]='bounced'
this is sample of desired data:
id email name emailFromTable2 emailstatus
1 test2#yahoo.com lili test2#yahoo.com bounced
2 tesere#yahoo.com mike Null Null
3 tedfd2#yahoo.com nik tedfd2#yahoo.com bounced
4 tdfdft2#yahoo.com sam Null Null
5 tedft2#yahoo.com james tedft2#yahoo.com bounced
6 tedft2#yahoo.com San Null
Use a nested select for this type of query. I would write this as:
select id, application, load, firstname, lastname, email,
(case when BouncedEmail is not null then email end) as EmailFromTable2,
BouncedEmail
from (SELECT A.[Id], A.[Application], A.[Loan], A.[Firstname], A.[Lastname], A.[Email],
(case when exists (select 1
from tbl_WebHook h
where A.Email = H.Email and H.[event] = 'bounced'
)
then 'bounced
end) as BouncedEmail
FROM Applicant A (NOLOCK)
) a
You can also do this with cross apply, but because you only really need one column, a correlated subquery also works.
;WITH DistinctEmails
AS
(
SELECT * , rn = ROW_NUMBER() OVER (PARTITION BY [Email] ORDER BY [Email])
FROM [tbl_Webhook]
)
SELECT
A.[Id]
,A.[Application]
,A.[Loan]
,A.[Firstname]
,A.[Lastname]
,A.[Email],
,H.[Email], H.[EmailStatus] as BouncedEmail
FROM Applicant A (NOLOCK) left outer join DistinctEmails [H] (NOLOCK)
on A.Email = H.Email
WHERE H.rn = 1
and H.[event]='bounced'
i believe query below should be enough to select distinct bounced email for you, cheer :)
SELECT
A.[Id]
,A.[Application]
,A.[Loan]
,A.[Firstname]
,A.[Lastname]
,A.[Email],
,H.[Email], H.[EmailStatus] as BouncedEmail
FROM Applicant A (NOLOCK)
Inner join [tbl_Webhook] [H] (NOLOCK)
on A.Email = H.Email
and H.[EmailStatus]='bounced'
basically i just change the joining to inner join and change the 2nd table condition from event to emailstatus, if u can provide your table structure and sample data i believe i can help you up :)