SQL Selecting Max Value 4 Joins - sql

I´m stuck with this Query. I want to get the following information from my Database:
User.username, Satz.Gewicht, Satz.Wiederholungen, ubungen.Name and Training.Datum
The Clue is, that I want to get this Columns for every different Exercise (ubungen.id).
I Tried with this:
SELECT
a3.Datum,
a4.username,
a2.Name,
max(Gewicht) as Gewicht,
Wiederholungen
from satz a1
INNER JOIN ubungen a2 ON a1.UBID = a2.ID
INNER JOIN training a3 ON a1.TID = a3.ID
INNER JOIN user a4 ON a3.UID = a4.ID
GROUP BY a1.UBID
But Somehow I´m getting the right Weight -> max(Gewicht) , but the wrong user.
What am I doing wrong?
Here is a Screenshot of my database design:
EDIT:
When using every column in my Group by I get Multiple Columns like this:
But i Just want the one in the middle at it is the one with the highest Gewicht.
Example of what I want:
This is the whole resultset after Joining the information I want. Now I just want the Lines that are marked in red. There are some lines having the same "max(gewicht)", no matter which of the ones to pick.

You have to use all the columns in the GROUP BY except aggregated function column
Try this out:
SELECT
a3.Datum, a4.username, a2.Name, max(Gewicht) as Gewicht, Wiederholungen
FROM satz a1
INNER JOIN ubungen a2
ON a1.UBID = a2.ID
INNER JOIN training a3
ON a1.TID = a3.ID
INNER JOIN user a4
ON a3.UID = a4.ID
GROUP BY
a3.Datum, a4.username, a2.Name, Wiederholungen

I think you are just looking for the record with the maximum value for each ubid:
select x.Ubid,x.Datum,x.username,x.name,x.Gewicht,x.Wiederholungen
from
(SELECT
a1.Ubid,
a3.Datum,
a4.username,
a2.Name,
Gewicht,
Wiederholungen,
row_number() over (partition by a1.ubid order by Gewicht desc) as rownum1
from satz a1
INNER JOIN ubungen a2 ON a1.UBID = a2.ID
INNER JOIN training a3 ON a1.TID = a3.ID
INNER JOIN user a4 ON a3.UID = a4.ID ) x
where x.rownum1=1
I have made use of the row_number function to pull the record with the highest Gewicht value. Hope this helps.
Edit: Adding a db-fiddle, it's working fine in this example:
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=0ac9caf422a06feb6e7c05c1a68995f5
Since row_number is not working, you can use an inner join to get the max value:
select x.Ubid,x.Datum,x.name,x.Gewicht,max(x.Wiederholungen) as Wiederholungen,max(x.username) as username
from
(SELECT
a1.Ubid,
a3.Datum,
a4.username,
a2.Name,
a1.Gewicht,
a1.Wiederholungen
from satz a1
INNER JOIN ubungen a2 ON a1.UBID = a2.ID
INNER JOIN training a3 ON a1.TID = a3.ID
INNER JOIN user a4 ON a3.UID = a4.ID ) x
INNER JOIN
(select ubid,max(Gewicht) as max_Gewicht from satz group by ubid) y
ON x.ubid=y.ubid and x.Gewicht=y.max_Gewicht
Group by x.Ubid,x.Datum,x.name,x.Gewicht

Your query runs, but it's malformed.
Columns not specified in the GROUP BY clause should be aggregated. MariaDB (and MySQL) are notorious by allowing non-aggregated columns in the SELECT clause.
Solution: Just add all non-aggregated columns to the GROUP BY clause to solve your issue, as in:
GROUP BY a1.UBID, a3.Datum, a4.username, a2.Name, Wiederholungen
As it is, MariaDB is selecting values randomly. Today you see the wrong values, but maybe tomorrow they are good. You don't want a query behaving like this.

Related

How to make a join on a table in an sql query that contains several others?

I am a beginner in SQL, I have an sql query that returns information I need on a table, the table ARTICLE_MODE:
GA_CODEARTICLE
C1
C2
C3
GA_LIBELLE
C5
C6
GA_LIBREART3
GA_LIBREART5
BUTSS5-RC
SURF HARD WARE
-
Wetsuits
DAY COVER
2021
UNISEXE
SURF
SOF
I need to retrieve information on a column of a second table.
The column MZS_DPAETAST of the table MTMPTVGEN.
In these two tables, two columns contain some identical information:
The GA_CODEARTICLE column from the ARTICLE_MODE table.
The column MZS_ARTICLE of the table MTMPTVGEN.
GA_CODEARTICLE
MZS_ARTICLE
BUTSS5-RC
BUTSS5-RC
BUTS85-RC
BUTS85-RC
BUTS75-RC
VMA045-VC
I tried to do this query to retrieve the values of the column MZS_DPAETAST which have as common values GA_CODEARTICLE and MZS_ARTICLE, it returns me many results:
select MZS_DPAETAST from MTMPTVGEN LEFT OUTER JOIN ARTICLE_MODE on MZS_ARTICLE=GA_CODEARTICLE
But how can I insert it in my initial query? Thanks for your help.
SELECT GA_CODEARTICLE, CC1.CC_LIBELLE AS C1,
YX2.YX_LIBELLE AS C2,
YX3.YX_LIBELLE AS C3,
GA_LIBELLE,
CC4.CC_LIBELLE AS C5,
CC5.CC_LIBELLE AS C6,
CC6.CC_LIBELLE AS C15,
GA_LIBREART3,
GA_LIBREART5
FROM ARTICLE_MODE
LEFT OUTER JOIN PGI_LOOKUP(GCFAMILLENIV1) CC1 ON GA_FAMILLENIV1=CC1.CC_CODE
AND CC1.CC_TYPE="FN1"
LEFT OUTER JOIN PGI_LOOKUP(GCLIBREART1) YX2 ON GA_LIBREART1=YX2.YX_CODE
AND YX2.YX_TYPE="LA1"
LEFT OUTER JOIN PGI_LOOKUP(GCLIBREART2) YX3 ON GA_LIBREART2=YX3.YX_CODE
AND YX3.YX_TYPE="LA2"
LEFT OUTER JOIN PGI_LOOKUP(GCCOLLECTION) CC4 ON GA_COLLECTION=CC4.CC_CODE
AND CC4.CC_TYPE="GCO"
LEFT OUTER JOIN PGI_LOOKUP(GCFAMILLENIV2) CC5 ON GA_FAMILLENIV2=CC5.CC_CODE
AND CC5.CC_TYPE="FN2"
LEFT OUTER JOIN PGI_LOOKUP(GCFAMILLENIV5) CC6 ON GA2_FAMILLENIV5=CC6.CC_CODE
AND CC6.CC_TYPE="FN5"
WHERE (GA_EMBALLAGE<>"X"
AND (GA_TYPEARTICLE NOT IN ("PRE","FI","FRA","UL","PAC"))
AND ((GA_STATUTART="GEN")))
ORDER BY GA_DATEMODIF DESC
It is hard to uderstand what you are having problems with. You are showing you know how to join article_mode and mtmptvgen. You have a query containing article_mode. So what keeps you from joining mtmptvgen there?
Your query has many outer joins. I cannot know whether these are really necessary. I don't now for mtmptvgen either. I am showing an outer join, but you can make this an inner join if that suffices of course.
SELECT
am.ga_codearticle,
cc1.cc_libelle as c1,
yx2.yx_libelle as c2,
yx3.yx_libelle as c3,
am.ga_libelle,
cc4.cc_libelle as c5,
cc5.cc_libelle as c6,
cc6.cc_libelle as c15,
am.ga_libreart3,
am.ga_libreart5,
m.mzs_dpaetast
FROM article_mode am
LEFT OUTER JOIN pgi_lookup(gcfamilleniv1) cc1 ON am.ga_familleniv1 = cc1.cc_code AND cc1.cc_type = 'FN1'
LEFT OUTER JOIN pgi_lookup(gclibreart1) yx2 ON am.ga_libreart1 = yx2.yx_code AND yx2.yx_type = 'LA1'
LEFT OUTER JOIN pgi_lookup(gclibreart2) yx3 ON am.ga_libreart2 = yx3.yx_code AND yx3.yx_type = 'LA2'
LEFT OUTER JOIN pgi_lookup(gccollection) cc4 ON am.ga_collection = cc4.cc_code AND cc4.cc_type = 'GCO'
LEFT OUTER JOIN pgi_lookup(gcfamilleniv2) cc5 ON am.ga_familleniv2 = cc5.cc_code AND cc5.cc_type = 'FN2'
LEFT OUTER JOIN pgi_lookup(gcfamilleniv5) cc6 ON am.ga2_familleniv5 = cc6.cc_code AND cc6.cc_type = 'FN5'
LEFT OUTER JOIN mtmptvgen m ON m.mzs_article = am.ga_codearticle
WHERE am.ga_emballage <> 'X'
AND am.ga_typearticle NOT IN ('PRE', 'FI', 'FRA', 'UL', 'PAC')
AND am.ga_statutart = 'GEN'
ORDER BY am.ga_datemodif DESC;
(I've added the missing column qualifiers and replaced the inappropriate double quotes with single quotes. These are supposed to be string literals, not column names, right? I also think that it makes queries hard to read when everything is in upper case and there are no spaces between operators, column names, and literals.)
UPDATE
You say that there is just one mzs_dpaetast value per article_mode, but the data model is inappropriate and stores it redundantly in every related mtmptvgen row. In order to deal with this, you'll want to select one mtmptvgen row per article_mode row only. One way to do so is to join a query instead of the table:
LEFT OUTER JOIN
(
SELECT mzs_article, MAX(mzs_dpaetast) AS mzs_dpaetast
FROM mtmptvgen
GROUP BY mzs_article
) m ON m.mzs_article = am.ga_codearticle

select top 1 in subquery?

I'm trying to get a top 1 returned for each code in this query but this is giving me syntax errors. Any ideas what I can do?
SELECT d.doc_no,
d.doc_short_name,
d.doc_name,
r.revision_code,
r.revision,
r.description,
dtg.group_name,
(SELECT top 1 di.index_user_id
FROM document_instance
WHERE doc_no = d.doc_no
ORDER BY entry_time DESC) AS 'last indexed by'
FROM documents d
LEFT JOIN document_revision r ON r.doc_no = d.doc_no
LEFT JOIN document_instance di ON di.doc_no = d.doc_no
LEFT JOIN document_type_group dtg ON dtg.doc_no = d.doc_no
Assuming Sybase ASE, the top # clause is only supported in derived tables, ie, it's not supported in correlated sub-queries like you're attempting.
Also note that order by is not supported in any sub-queries (derived table or correlated).
If I'm reading the query correctly, you want the index_user_id for the record with the max(entry_time); if this is correct, and assuming a single record is returned for a given doc_no/entry_time combo, you could try something like:
SELECT d.doc_no,
d.doc_short_name,
d.doc_name,
r.revision_code,
r.revision,
r.description,
dtg.group_name,
(SELECT d2.index_user_id
FROM document_instance d2
WHERE d2.doc_no = d1.doc_no
and d2.entry_time = (select max(d3.entry_time)
from document_instance d3
where d3.doc_no = d1.doc_no)
) as 'last indexed by'
FROM documents d
LEFT JOIN document_revision r ON r.doc_no = d.doc_no
LEFT JOIN document_instance d1 ON d1.doc_no = d.doc_no
LEFT JOIN document_type_group dtg ON dtg.doc_no = d.doc_no

MS Access (via VB.NET) - convert linked attribute tables to columns in output

I've searched, and found a lot of similar things, but not quite what I'm looking for.
I have what are essentially a bunch of linked tables with attributes and values.
xxBio table
xxBio_ID xxBio_LINK
1 100
2 101
xx table
xxBio_LINK xxAttr_LINK
100 1000
101 2000
xxAttr table
xxAttr_LINK xxAttrCat_1_LINK xxAttrCat_2_LINK
1000 null 550
2000 650 null
xxAttrCat_1 table
xxAttrCat_1_LINK xxAttrCat_1_Description xxAttrCat_1_Value
650 wumpus 20
xxAttrCat_2 table
xxAttrCat_2_LINK xxAttrCat_2_Description xxAttrCat_2_Value
550 frith 30
OUTPUT NEEDS TO BE:
xxBio_ID frith wumpus
1 30 null
2 null 20
I can easily see how to get the result set with columns like attribute_name1, attribute_value1, attribute_name2, attribute_value2, and so forth.
SELECT xxBio.ID, xxAttrCat_1.xxAttrCat_1_Description, xxAttrCat_1.xxAttrCat_1_Value, xxAttrCat_2.xxAttrCat_2_Description, xxAttrCat_2.xxAttrCat_2_Value
FROM ((
(xx INNER JOIN xxBio ON xx.xxBio_LINK = xxBio.xxBio_LINK)
INNER JOIN xxAttr ON xx.xxAttr_LINK = xxAttr.xxAttr_LINK)
LEFT JOIN xxAttrCat_1 ON xxAttr.xxAttrCat_1_LINK = xxAttrCat_1.xxAttrCat_1_LINK)
LEFT JOIN xxAttrCat_2 ON xxAttr.xxAttrCat_2_LINK = xxAttrCat_2.xxAttrCat_2_LINK
But that's not what we need. We need the attribute names to be column names.
How do we achieve this?
Update to question:
It turned out that we misunderstood the requirements, so we had to take a different route and did not get to try the answers. I appreciate the help.
Put the SQL in a string and execute from VBA. (Dynamic SQL?)
sql = "SELECT xxBio.ID, " & xxAttrCat_1.xxAttrCat_1_Description & ", " & ...
Consider using IIF() conditional statements:
SELECT xxBio.ID,
IIF(xxAttrCat_1.xxAttrCat_1_Description = 'wumpus',
xxAttrCat_1.xxAttrCat_1_Value, NULL) As [wumpus],
IIF(xxAttrCat_2.xxAttrCat_2_Description = 'frith',
xxAttrCat_2.xxAttrCat_2_Value, NULL) As [frith]
FROM (((xx
INNER JOIN xxBio ON xx.xxBio_LINK = xxBio.xxBio_LINK)
INNER JOIN xxAttr ON xx.xxAttr_LINK = xxAttr.xxAttr_LINK)
LEFT JOIN xxAttrCat_1 ON xxAttr.xxAttrCat_1_LINK = xxAttrCat_1.xxAttrCat_1_LINK)
LEFT JOIN xxAttrCat_2 ON xxAttr.xxAttrCat_2_LINK = xxAttrCat_2.xxAttrCat_2_LINK
If each ID can have multiple values along same descriptions and you need to sum or average all corresponding values, then turn query into conditional aggregation (known as pivoting). Below also uses table aliases, a, b, c1, c2 for brevity and organization.
SELECT b.ID,
SUM(IIF(c1.xxAttrCat_1_Description='wumpus', c1.xxAttrCat_1_Value, NULL)) As [wumpus],
SUM(IIF(c2.xxAttrCat_2_Description='frith', c2.xxAttrCat_2_Value, NULL)) As [frith]
FROM (((xx
INNER JOIN xxBio b ON xx.xxBio_LINK = b.xxBio_LINK)
INNER JOIN xxAttr a ON xx.xxAttr_LINK = a.xxAttr_LINK)
LEFT JOIN xxAttrCat_1 c1 ON a.xxAttrCat_1_LINK = c1.xxAttrCat_1_LINK)
LEFT JOIN xxAttrCat_2 c2 ON a.xxAttrCat_2_LINK = c2.xxAttrCat_2_LINK
GROUP BY b.ID
And if such descriptions can be many use MS Access SQL's crosstab query. But since you have two categories, run a crosstab on each and save them as stored queries or views. Then, join together:
--CATEGORY 1
TRANSFORM SUM(c1.xxAttrCat_1_Value) AS SumOfValue
SELECT b.ID,
FROM ((xx
INNER JOIN xxBio b ON xx.xxBio_LINK = b.xxBio_LINK)
INNER JOIN xxAttr a ON xx.xxAttr_LINK = a.xxAttr_LINK)
LEFT JOIN xxAttrCat_1 c1 ON a.xxAttrCat_1_LINK = c1.xxAttrCat_1_LINK
GROUP BY b.ID
PIVOT c1.xxAttrCat_1_Description;
--CATEGORY 2
TRANSFORM SUM(c2.xxAttrCat_2_Value) AS SumOfValue
SELECT b.ID,
FROM ((xx
INNER JOIN xxBio b ON xx.xxBio_LINK = b.xxBio_LINK)
INNER JOIN xxAttr a ON xx.xxAttr_LINK = a.xxAttr_LINK)
LEFT JOIN xxAttrCat_2 c2 ON a.xxAttrCat_2_LINK = c2.xxAttrCat_2_LINK
GROUP BY b.ID
PIVOT c2.xxAttrCat_2_Description;
--CROSSTAB QUERIES JOIN
SELECT c1.*, c2.*
FROM Categ1CrossTabQ c1
INNER JOIN Categ1CrossTabQ c2
ON c1.ID = c2.ID;
Please note: comments above with double dash are not allowed in an Access query and only one statement is allowed in saved query. Access has a limit of 255 columns per table/query, so pivoted values greater than 255 will err out unless you use IN clause. Finally, you cannot use crosstabs as subqueries. And crosstabs are strictly an MS Access command and not used in other DBMS's.

SQL JOIN Condition moved to with where clause produces differences

Query 1
select count(1)
from sdb_snmp_sysdata s
left join sdb_snmp_entphysicaltable e on s.source = e.source **and e.class = 3**
left join SDB_DF_DEVICE_DNS dns on dns.source = s.source
left join sdb_fdb_node f on upper(f.oldnodeid) = upper(dns.dns_name)
where (regexp_like(s.descr, 'NFXS-F FANT-F ALCATEL-LUCENT|Motorola APEX3000')
or regexp_like(e.descr, 'Motorola BSR64000 HD 100A Redundant Chassis|AS2511-RJ chassis')
or trim(e.ModelName) in ('RFGW1', 'ARCT01949', 'ARCT03253', 'UBR10012', 'WS-C3750-48TS-S', 'WS-C3750V2-48TS-S')
or e.name like '%Nexus5596 Chassis%')
Query 2:
select count(1)
from sdb_snmp_sysdata s
left join sdb_snmp_entphysicaltable e on s.source = e.source
left join SDB_DF_DEVICE_DNS dns on dns.source = s.source
left join sdb_fdb_node f on upper(f.oldnodeid) = upper(dns.dns_name)
where (regexp_like(s.descr, 'NFXS-F FANT-F ALCATEL-LUCENT|Motorola APEX3000')
or regexp_like(e.descr, 'Motorola BSR64000 HD 100A Redundant Chassis|AS2511-RJ chassis')
or trim(e.ModelName) in ('RFGW1', 'ARCT01949', 'ARCT03253', 'UBR10012', 'WS-C3750-48TS-S', 'WS-C3750V2-48TS-S')
or e.name like '%Nexus5596 Chassis%') **and e.class = 3**
The above two queries return different number of rows by changing e.class condition from on clause to where clause. I am unable to figure out. any help is appreciated.
My Understanding:
query 1 left outer join between sysdata and entphysicaltable hash join happens after full scan of individual tables.
in the second query 2 join happens after entphysicaltable is reduced to records containing only entphysicaltable.class = 3.
to me the query makes same sense but returns different results.
I can relate to this question I would like to know a concrete reason.
The best explanation is on a little example. Let have two tables
TABLE A
C1
----------
1
2
TABLE B
C1 C2
---------- -
1 x
Then the query with the filter B.c2 = 'x' in the ON clause returns 2 rows
select *
from A left outer join B
on A.c1 = B.c1 and B.c2 = 'x';
C1 C1 C2
---------- ---------- --
1 1 x
2
while when the filter is moved in the WHERE clause, only one row is delivered
select *
from A left outer join B
on A.c1 = B.c1
WHERE B.c2 = 'x';
C1 C1 C2
---------- ---------- --
1 1 x
The WHERE clause simple overrules the OUTER JOIN row missing logik - wee all know that NULL is not equal 'x', so the second row is discarded.
BWT if you see in the old join syntax constructs like B.c2(+) = 'x' this is the very same thema.
If I read your question right, then it simply comes down to how a LEFT JOIN works.
The way a (outer) LEFT JOIN works is that it will join what's on your left side with what's on your right side.
And then it being an outer join it will try to add NULL values to the right, for the situation where there is no match on the right.
However, by you adding your constraints in the WHERE clause, you're telling the query engine to filter out the rows where there is NULL because they will not match your WHERE clause.
If you have the filters in your ON clause - the query engine will not remove/filter out the NULL rows.
This happens because the WHERE is 'executed' after the JOINs.
That's why you get different number of rows, because an OUTER join functions differently based on whether you use the ON or the WHERE clause.
So if you want the join to include NULL rows, you'll need to use the ON clause.

Order By Upper not sorting correctly after a set number of records get returned

The 2 queries below are exactly the same except for the "Not In" condition.
The following query yields a correct sorting of the following names:
Query:
Select (Bp.Last_Name||', '||Bp.First_Name||' '||Substr(Bp.Middle_Name,1,1)) As Username
From Scrty.User_Account Ua
Inner Join Buspty.Business_Person Bp On Ua.Business_Person_Uuid = Bp.Business_Party_Uuid And Ua.Active_Flag = 'Y' And Bp.Last_Name Not In (**'a2s','Abdallah','Abnoosi','Abrahamson','Abrams'**) And Ua.Type = 'APPLICATION'
Inner Join Scrty.User_Account_Aaa Uaa On Ua.Uuid = Uaa.User_Account_Uuid
Left Outer Join Buspty.Business_Organization Bo On Bo.Business_Party_Uuid = Bp.Primary_Department_Uuid
Left Outer Join Buspty.Business_Org_Type_Domain Botd On Bo.Business_Org_Type_Uuid = Botd.Uuid And Botd.Code =2 Left Outer Join Buspty.Network N On Bp.Primary_Network_Uuid = N.Uuid
Order By Upper(Username)
Result:
TAYLOR, BRANDON
Taylor, Brandon
However, when I exclude one more names in the "Not In" condition, the query results in the lowercase brandon taylor coming first.
query:
Select (Bp.Last_Name||', '||Bp.First_Name||' '||Substr(Bp.Middle_Name,1,1)) As Username
From Scrty.User_Account Ua
Inner Join Buspty.Business_Person Bp On Ua.Business_Person_Uuid = Bp.Business_Party_Uuid And Ua.Active_Flag = 'Y' And Bp.Last_Name Not In (**'a2s','Abdallah','Abnoosi','Abrahamson','Abrams','Abruzzo'**) And Ua.Type = 'APPLICATION'
Inner Join Scrty.User_Account_Aaa Uaa On Ua.Uuid = Uaa.User_Account_Uuid
Left Outer Join Buspty.Business_Organization Bo On Bo.Business_Party_Uuid = Bp.Primary_Department_Uuid
Left Outer Join Buspty.Business_Org_Type_Domain Botd On Bo.Business_Org_Type_Uuid = Botd.Uuid And Botd.Code =2 Left Outer Join Buspty.Network N On Bp.Primary_Network_Uuid = N.Uuid
Order By Upper(Username)
Result:
Taylor, Brandon
TAYLOR, BRANDON
You are ordering by identical values and thus the database is free to choose any order it wants. It's essentially as if you had no order by at all (this is actually a nice example on how a database changes the sort order based on conditions in the query - another proof that you should never rely on any implicit order in a query result)
To get a consistent sort order you need to include some unique column into the order by clause:
Order By Upper(Username), Ua.Uuid -- or whatever makes the combination unique
that will guarantee the same sort order if two usernames (uppercased) are the same. You could also include a timestamp value or something else.