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

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);

Related

How to fix sql query problem with two or more position which have one ID

I have sql query where I have to join three tables. One of this is a table with data of invoice, it looks like this:
INVOICE
ID CUSTOMER_NAME TAXID NUMBER LABEL GUID
1 CUSTOMER1 8739281100 FV001/2019 1 04EABFB3-0B9D-4749-B99D-A4EBEE079633
POSITION OF INVOICE
ID ID_INV POSITION_NAME COUNT
1 1 NAME1 3
2 1 NAME2 2,5
TABLE WITH LABEL
ID NAME VALUE GUID_INV
1 LABEL1 true 04EABFB3-0B9D-4749-B99D-A4EBEE079633
When I want to run this query I have statement like this multiple rows in singleton select.
This is for Firebird 2.5.
SELECT
a.ID,
a.GUID,
a.NUMBER,
a.CUSTOMER_NAME,
b.COUNT,
(select usrd.LABEL from USER_FIELD_DEFS usrd
where usrd.GUID_INV=a.GUID and (usrd.ID=1 and usrb.VALUE='true')) as LABEL_NAME
FROM INVOICE a
join POSITION_INVOICE b ON a.ID=b.ID_INV
I want to get result like this
1 04EABFB3-0B9D-4749-B99D-A4EBEE079633 FV001/2019 CUSTOMER1 3 LABEL1
1 04EABFB3-0B9D-4749-B99D-A4EBEE079633 FV001/2019 CUSTOMER1 2,5 LABEL1
Please help with this. I know that solution maybe is very simple but I have some eclipse of the mind:)
This should give you the rows you want based on the 3 tables you provided. If there is a chance that an invoice has no position then simply replace the inner join with left join
SELECT
I.[Id]
,I.[GUID]
,I.[NUMBER]
,I.[CUSTOMER_NAME]
,IP.[POSITION_NAME]
,L.[NAME]
FROM [INVOICE] I
INNER JOIN [IN_P] IP ON IP.ID_INV = I.Id
LEFT JOIN [LABEL] L ON L.[GUID_INV] = I.[GUID]
You are just missing one more join here. Assuming USER_FIELD_DEFS is the same as TABLE WITH LABEL that you have mentioned here
SELECT
a.ID,
a.GUID,
a.NUMBER,
a.CUSTOMER_NAME,
b.COUNT,
c.NAME
FROM INVOICE a
JOIN POSITION_INVOICE b ON a.ID=b.ID_INV
JOIN USER_FIELD_DEFS c ON c.GUID_INV = a.GUID AND c.ID=1 and c.VALUE='true'

JOIN query, SQL Server is dropping some rows of my first table

I have two tables customer_details and address_details. I want to display customer details with their corresponding address, so I was using a LEFT JOIN, but when I'm executing this query, SQL Server drops rows where street_no of customer_details table doesn't match with the street_no in address_detials table and displays only rows where `street_no' of customer_detials = street_no of address_details table. I need to display a complete customer_details table and in case if street_no doesn't matches it should display empty string or anything. Am I doing anything wrong in my SQL join?
Table customer_details:
case_id customer_name mob_no street_no
-------------------------------------------------
1 John 242342343 4324234234234
1 Rohan 343233333 43332
1 Ankit 234234233 2342332423433
1 Suresh 234234324 2342342342342
1 Ranjeet 343424323 32233
1 Ramu 234234333 2342342342343
Table address_details:
s_no streen_no address city case_id
------------------------------------------------------
1 4324234234234 Roni road Delhi 1
2 2342332423433 Natan street Lucknow 1
3 2342342342342 Koliko road Herdoi 1
SQL JOIN query:
select
a.*, b.address
from
customer_details a
left join
address_details b on a.street_no = b.street_no
where
b.case_id = 1
Now that it became clear that you used b.case_id=1, I will explain why it filters:
The LEFT JOIN itself returns some rows that contain all NULL values for table b in the result set, which is what you want and expect.
But by using WHERE b.case_id=1, the rows containing NULL values for table b are filtered out because none of them matches the condition (all those rows have b.case_id=NULL so they don't match).
It might work to instead use WHERE a.case_id=1, but we don't know if a.case_id and b.case_id are always the same value for matching rows (they might not be; and if they are always the same, then we just identified a potential redundancy).
There are two ways to fix this for sure.
(1) Move b.case_id = 1 into the left join condition:
left join address_details b on a.street_no = b.street_no and b.case_id = 1
(2) Keep b.case_id = 1 in the WHERE but also allow for NULLED-out b values:
left join address_details b on a.street_no = b.street_no
where b.case_id = 1
or b.street_no IS NULL
Personally I'd go for (1) because that is the most clear way to express that you want to filter b on two conditions, without affecting the rows of a that are being returned.
I do think that Wilhelm Poggenpohl answer is kind of right. You just need to change the last join condition a.case_id=1 to b.case_id=1
select a.* , b.address
from customer_details a
left join address_details b on a.street_no=b.street_no
and b.case_id=1
This query will show every row from customer_details and the corresponding adress if there is a match of street_no and the adress meets the condition case_id=1.
This is because of the where clause. Try this:
select a.* , b.address
from customer_details a
left join address_details b on a.street_no=b.street_no
and a.case_id=1

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

Oracle Statement does not count correctly

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

Left Outer Join with one result per match and "priority" of match set by a different field in match SQL Server 2005

I am trying to get a single result (PHONE) per match (CONTACT_ID) in a Left Outer Join. I imagine that there is a way to accomplish this with the preference (or order) being set by another column/field- the phone type (TYPE), but I haven't been able to figure it out. Below is a list of facts to help better explain what I am trying to accomplish and then following is an example Table A and B with the desired result. I've looked at min() and group by, but I don't know how to make those work here. As a side note, after this is working, I will be joining it to more tables to the left of it in a simpler fashion.
The student can have an unlimited number of CONTACT_ID.
A contact does not always have all phone types.
The preferred order of phone types (TYPE) is C,H,W (which, fortunately, happens to be alphabetical)
ignore match and go to the next in priority if PHONE is null
TableA:
STUDENT_ID CONTACT_ID
---------- ----------
X 1
X 2
Y 3
Y 4
TableB:
CONTACT_ID TYPE PHONE
---------- ---- -----
1 H 21
1 C
1 W 44
2 H 78
2 C 92
2 W 11
Desired Result:
STUDENT_ID CONTACT_ID TYPE PHONE
---------- ---------- ---- -----
X 1 H 21
X 2 C 92
Y 3
Y 4
Here is the query that I have that will make a join with all phone matches (minus all of my crazy attempts at getting what I want).
SELECT *
FROM Table TableA T1
LEFT OUTER JOIN TableB T2 ON T1.CONTACT_ID = T2.CONTACT_ID
All help greatly appreciated!
Edited code from Stefan Onofrei's solution:
(results in some duplicate entries)
SELECT
T1.STUDENT_ID,
T1.CONTACT_ID,
T2.PHONE_TYPE,
T3.PHONE
FROM REG_STU_CONTACT T1
INNER JOIN
(SELECT MIN(PHONE_TYPE) AS PHONE_TYPE, CONTACT_ID
FROM REG_CONTACT_PHONE
WHERE PHONE IS NOT NULL
GROUP BY CONTACT_ID) T2 ON T1.CONTACT_ID = T2.CONTACT_ID
INNER JOIN REG_CONTACT_PHONE T3 ON T2.CONTACT_ID = T3.CONTACT_ID AND T2.PHONE_TYPE = T3.PHONE_TYPE
ORDER BY T1.STUDENT_ID
Select A.STUDENT_ID A.CONTACT_ID B.TYPE c.PHONE
from TableA A
inner join
(select MIN(type ) as type, Contact_ID
from Tableb
where phone is not null
group by contactid) B
on A.contactid = b.contactid
inner join Tableb C
on B.contactid = c.conatctid and b.type = c.type