Join & Group By - Column is invalid in the select list - sql

I'm running a query to join 2 tables and then group the rows by 2 fields and select the rows with the min ID from these groups and I get an error. The joined table looks like:
+--------+---------+-----------------+-----------+-----------+
| ID | CODE | NAME | VRACHAR01 | VRACHAR02 |
+--------+---------+-----------------+-----------+-----------+
| 290861 | 1110896 | PRODUCT NAME XX | 001 | 706 |
| 290864 | 1110899 | PRODUCT NAME XX | 001 | 706 |
| 290865 | 1110900 | PRODUCT NAME XX | 003 | 721 |
| 290870 | 1110905 | PRODUCT NAME XX | 004 | 743 |
| 290871 | 1110906 | PRODUCT NAME XX | 004 | 743 |
| 290878 | 1110913 | PRODUCT NAME XX | 006 | 806 |
| 290879 | 1110914 | PRODUCT NAME XX | 007 | 807 |
| 290908 | 1110943 | PRODUCT NAME XX | 008 | 815 |
+--------+---------+-----------------+-----------+-----------+
If I run the script below to group the results by the last 2 fields I get an error:
Column 'A.CODE' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
SELECT
min(A.ID),
A.CODE,
A.NAME,
B.VARCHAR01,
B.VARCHAR02
FROM
PRODUCTS A
INNER JOIN
EXTRAS B
ON
A.ID = B.ID
WHERE
A.COMPANY = 1002
AND
A.TYPE = 50
GROUP BY
B.VARCHAR01,
B.VARCHAR02
Any help is appreciated.

If you want the min id, then you don't need other columns:
SELECT MIN(P.ID), E.VARCHAR01, E.VARCHAR02
FROM PRODUCTS P INNER JOIN
EXTRAS E
ON P.ID = E.ID
WHERE P.COMPANY = 1002 AND P.TYPE = 50
GROUP BY E.VARCHAR01, E.VARCHAR02;
Note that this replaces the meaningless table aliases with abbreviations for the table names.
If you want the entire row, then you can use window functions:
SELECT PE.*
FROM (SELECT P.*, E.VARCHAR01, E.VARCHAR02,
ROW_NUMBER() OVER (PARTITION BY E.VARCHAR01, E.VARCHAR02 ORDER BY P.ID ASC) as seqnum
FROM PRODUCTS P INNER JOIN
EXTRAS E
ON P.ID = E.ID
WHERE P.COMPANY = 1002 AND P.TYPE = 50
) PE
WHERE seqnum = 1;

Related

SQL self join result of a select

I have two tables, lets call them A and B which I perform an inner join on.
select
A.id,
A.serial_number as serial,
concat(B.type, '-', B.primary, '-', B.secondary) as product_number, A.parent_id as parent
from A
inner join B on A.number_id = B.id) as T1
as a result I get a set that contains parents and children (1 per parent).
+----+--------+-----------------+--------+
| id | serial | product number | parent |
+----+--------+-----------------+--------+
| 1 | 123 | abc | null |
| 2 | 234 | cde | 1 |
| 3 | 456 | abc | null |
| 4 | 895 | cde | 2 |
+----+--------+-----------------+--------+
now I'd like to do a self join to get the following
+----+---------------+------------------------+---------------+-----------------------+
| id | serial parent | product_number parent | serial child | product_number child |
+----+---------------+------------------------+---------------+-----------------------+
| 1 | 123 | abc | 234 | cde |
| 2 | 456 | abc | 895 | cde |
+----+---------------+------------------------+---------------+-----------------------+
What would be the best approach for this, I simply couldn't find an easy solution... is there a way to join T1 with itself?
I think that's more joins:
select
ap.id as id_parent,
ap.serial_number as serial_parent,
concat_ws('-', bp.type, bp.primary, bp.secondary) as product_number_parent,
ac.child as id_child,
ac.serial_number as serial_child,
concat_ws('-', bc.type, bc.primary, bc.secondary) as product_number_child
from a ap
inner join a ac on ac.parent = ap.id
inner join b bp on bp.id = ap.astrol_number_id
inner join b bc on bc.id = ac.astrol_number_id
where ap.parent is null

Can someone help me figure out if I'm making a mistake in my query?

I'm trying to create a query that returns the names of all people in my database that have less than half of the money of the person with the most money.
These is my query:
select P1.name
from Persons P1 left join
AccountOf A1 on A1.person_id = P1.id left join
BankAccounts B1 on B1.id = A1.account_id
group by name
having SUM(B1.balance) < MAX((select SUM(B1.balance) as b
from AccountOf A1 left join
BankAccounts B1 on B1.id = A1.account_id
group by A1.person_id
order by b desc
LIMIT 1)) * 0.5
This is the result:
+-------+
| name |
+-------+
| Evert |
+-------+
I have the following tables in the database:
+---------+--------+--+
| Persons | | |
+---------+--------+--+
| id | name | |
| 11 | Evert | |
| 12 | Xavi | |
| 13 | Ludwig | |
| 14 | Ziggy | |
+---------+--------+--+
+--------------+---------+
| BankAccounts | |
+--------------+---------+
| id | balance |
| 11 | 525000 |
| 12 | 750000 |
| 13 | 1900000 |
| 14 | 1600000 |
+--------------+---------+
+-----------+-----------+------------+
| AccountOf | | |
+-----------+-----------+------------+
| id | person_id | account_id |
| 301 | 11 | 12 |
| 302 | 13 | 12 |
| 303 | 13 | 14 |
| 304 | 14 | 11 |
| 305 | 14 | 13 |
+-----------+-----------+------------+
What am I missing here? I should get two entries in the result (Evert, Xavi)
I wouldn't approach the logic this way (I would use window functions). But your final having has two levels of aggregation. That shouldn't work. You want:
having SUM(B1.balance) < (select 0.5 * SUM(B1.balance) as b
from AccountOf A1 join
BankAccounts B1 on B1.id = A1.account_id
group by A1.person_id
order by b desc
limit 1
)
I also moved the 0.5 into the subquery and changed the left join to a join -- the tables need to match to get balances.
I would recommend window functions, if your - undisclosed! - database supports them.
You can join and aggregate just once, and then use a window max() to get the top balance. All that is then left to is to filter in an outer query:
select *
fom (
select p.id, p.name, coalesce(sum(balance), 0) balance,
max(sum(balance)) over() max_balance
from persons p
left join accountof ao on ao.person_id = p.id
left join bankaccounts ba on ba.id = ao.account_id
group by p.id, p.name
) t
where balance > max_balance * 0.5

Postgresql left join

I have two tables cars and usage. I create a record in usage once a month for some of cars.
Now I want to get distinct list of cars with their latest usage that I saved.
first of all look at the tables please
cars:
| id | model | reseller_id |
|----|-------------|-------------|
| 1 | Samand Sall | 324228 |
| 2 | Saba 141 | 92933 |
usages:
| id | car_id | year | month | gas |
|----|--------|------|-------|-----|
| 1 | 2 | 2020 | 2 | 68 |
| 2 | 2 | 2020 | 3 | 94 |
| 3 | 2 | 2020 | 4 | 33 |
| 4 | 2 | 2020 | 5 | 12 |
The problem is here
I need only the latest usage of year and month
I tried a lot of ways but none of them is good enough. because sometimes this query gets me one ofnot latest records of usages.
SELECT * FROM cars AS c
LEFT JOIN
(select *
from usages
) u on (c.id = u.car_id)
order by u.gas desc
You can do this with a DISTINCT ON in the derived table:
SELECT *
FROM cars AS c
LEFT JOIN (
select distinct on (u.car_id) *
from usages u
order by u.car_id, u.year desc, u.month desc
) lu on c.id = lu.car_id
order by u.gas desc;
I think you need window function row_number. Here is the demo.
select
id,
model,
reseller_id
from
(
select
c.id,
model,
reseller_id,
row_number() over (partition by u.car_id order by u.id desc) as rn
from cars c
left join usages u
on c.id = u.car_id
) subq
where rn = 1

Oracle SQL - Select duplicates based on two columns

I need to select duplicate rows based on two columns in a join, and i can't seem to figure out how that is done.
Currently i got this:
SELECT s.name,administrative_site_id as adm_id,s.external_code,si.identifier_value
FROM suppliers s
INNER JOIN suppliers_identifier si
ON s.id = si.supplier_id
And the output is something along the lines of below:
| Name | adm_id | external_code |identifier_value |
|:-----------|------------:|:------------: |:----------------:|
| Warlob | 66323 | ext531 | id444 |
| Ozzy | 53123 | ext632 | id333 |
| Motorhead | 521 | ext733 | id222 |
| Perez | 123 | ext833 | id111 |
| Starlight | 521 | ext934 | id222 |
| Aligned | 123 | ext235 | id111 |
What i am looking for, is how to simply select these 4 rows, as they are duplicates based on column: adm_id and Identifier_value
| Name | adm_id | external_code |identifier_value |
|:-----------|------------:|:------------: |:----------------:|
| Motorhead | 521 | ext733 | id222 |
| Perez | 123 | ext833 | id111 |
| Starlight | 521 | ext934 | id222 |
| Aligned | 123 | ext235 | id111 |
First group by ADM_ID, IDENTIFIER_VALUE and find groups that has more than one row in it.
Then select all rows that has these couples
SELECT S.NAME
,ADMINISTRATIVE_SITE_ID AS ADM_ID
,S.EXTERNAL_CODE
,SI.IDENTIFIER_VALUE
FROM SUPPLIERS S INNER JOIN SUPPLIERS_IDENTIFIER SI ON S.ID = SI.SUPPLIER_ID
WHERE (ADMINISTRATIVE_SITE_ID, SI.IDENTIFIER_VALUE) IN (SELECT ADMINISTRATIVE_SITE_ID AS ADM_ID, SI.IDENTIFIER_VALUE
FROM SUPPLIERS S INNER JOIN SUPPLIERS_IDENTIFIER SI ON S.ID = SI.SUPPLIER_ID
GROUP BY ADM_ID, IDENTIFIER_VALUE
HAVING COUNT(*) > 1)
Or an alternate way that may perform better on big datasets:
with t as (
SELECT s.name,administrative_site_id as adm_id,s.external_code,si.identifier_value
COUNT(*) OVER (PARTITION BY administrative_site_id ,identifier_value ) AS cnt
FROM suppliers s
INNER JOIN suppliers_identifier si
ON s.id = si.supplier_id)
select name, adm_id, external_code, identifier_value
from t
where cnt > 1

msaccess join most recent matching record from one table to another with multiple criteria

Expanding upon answer on msaccess join most recent matching record from one table to another
SELECT c.Tag, c.DateCreated AS most_recent, c.Comment, c.Author
FROM
(
SELECT Tag, MAX(DateCreated) AS MaxDate
FROM Comments
GROUP BY Tag
) AS md
INNER JOIN
Comments AS c
ON c.Tag = md.Tag AND c.DateCreated = md.MaxDate
How do I use multiple criteria / n+1 criteria to get the result eg: I want to use tag, critera1 and criteria2 as a single composite key to query the status and comment tables.
EDIT: I've modified the question as what I Was originally asking apparently could not be done by storing the source data in a single table, so I'll ask another question with the data separated into separate tables. This question has been modified to match the answer given by #DonJewett
Table - Result
+----+--------+-----------+-----------+----------------+-------------+--------+
| ID | Tag | Criteria1 | Criteria2 | DateCreated | Comment | Author |
+----+--------+-----------+-----------+----------------+-------------+--------+
| 6 | TAG001 | ghi | jkl | 25-July-2015 | Something6 | AQ |
| 8 | TAG001 | mno | pqr | 23-July-2015 | Something8 | BV |
| 13 | TAG002 | abc | abc | 22-June-2015 | Something13 | BV |
| 14 | TAG001 | abc | def | 06-August-2015 | Something14 | AB |
+----+--------+-----------+-----------+----------------+-------------+--------+
The tables I have:
Status Table
+--------+-------------+-----------+-----------+----------------+
| Tag | Status | Criteria1 | Criteria2 | DateStatus |
+--------+-------------+-----------+-----------+----------------+
| TAG001 | Not Started | abc | def | 04-August-2015 |
| TAG001 | Complete | ghi | jkl | 04-August-2015 |
| TAG001 | Complete | mno | pqr | 02-August-2015 |
| TAG002 | Not Started | abc | abc | 02-August-2015 |
+--------+-------------+-----------+-----------+----------------+
Comments Table:
+----+--------+-----------+-----------+----------------+-------------+--------+
| ID | Tag | Criteria1 | Criteria2 | DateCreated | Comment | Author |
+----+--------+-----------+-----------+----------------+-------------+--------+
| 1 | TAG001 | abc | def | 22-July-2015 | Something1 | JS |
| 3 | TAG001 | abc | def | 23-July-2015 | Something3 | AM |
| 6 | TAG001 | ghi | jkl | 25-July-2015 | Something6 | AQ |
| 8 | TAG001 | mno | pqr | 23-July-2015 | Something8 | BV |
| 12 | TAG002 | abc | abc | 20-June-2015 | Something12 | AZ |
| 13 | TAG002 | abc | abc | 22-June-2015 | Something13 | BV |
| 14 | TAG001 | abc | def | 06-August-2015 | Something14 | AB |
+----+--------+-----------+-----------+----------------+-------------+--------+
I tried using AND, but it didn’t work.
SELECT c.Tag, c.DateCreated AS most_recent, c.Comment, c.Author
FROM
(
SELECT Tag, MAX(DateCreated) AS MaxDate
FROM Comments
GROUP BY Tag
) AS md
INNER JOIN
Comments AS c
ON c.Tag = md.Tag AND c.DateCreated = md.MaxDate AND status.critera1 = c.criteria1 AND status.criteria2 = c.criteria2
COMPLETE ANSWER:
MaxCommentDateQuery
SELECT Tag, MAX(DateCreated) AS MaxDate, Criteria1, Criteria2
FROM Comments
GROUP BY Tag, Criteria1, Criteria2;
ResultQuery
SELECT c.Tag, c.DateCreated AS most_recent, c.Comment, c.Author
FROM MaxCommentDateQuery AS md INNER JOIN Comments AS c ON (md.Tag = c.Tag) AND (md.MaxDate = c.DateCreated);
ResultQuery (now with Status)
SELECT DISTINCT c.*
FROM Status
INNER JOIN (MaxCommentDateQuery AS md
INNER JOIN Comments AS c
ON (md.Tag = c.Tag) AND (md.MaxDate = c.DateCreated) AND (c.criteria1 = md.criteria1) AND (c.criteria2 = md.criteria2))
ON (Status.Tag = c.Tag) AND (Status.Criteria1 = c.Criteria1) AND (Status.Criteria2 = c.Criteria2);
Try this:
SELECT c.Tag, c.DateCreated AS most_recent, c.Comment, c.Author
FROM
(
SELECT Tag, MAX(DateCreated) AS MaxDate
FROM Comments
GROUP BY Tag
) AS md
INNER JOIN
Comments AS c
ON c.Tag = md.Tag AND c.DateCreated = md.MaxDate
INNER JOIN
status AS s
ON s.critera1 = c.criteria1 AND s.criteria2 = c.criteria2
You don't have the Status table in your query, so you can not use it in your query. I would suggest breaking your subquery out into its own separate query, and then you will be able to edit using the UI.
MaxCommentDateQuery
SELECT Tag, MAX(DateCreated) AS MaxDate
FROM Comments
GROUP BY Tag
ResultQuery
SELECT c.Tag, c.DateCreated AS most_recent, c.Comment, c.Author
FROM MaxCommentDateQuery AS md
INNER JOIN Comments AS c
ON c.Tag = md.Tag AND c.DateCreated = md.MaxDate
Once you have these broken out, you will be able to add Status to the ResultQuery using the UI. Join on Tag and then you should be able use Status fields in your criteria.
Version 1
This is a composite key of Tag,Criteria1,Criteria2 joining Comments and Status. The result will be limited to the Maximum comment date. You may find this limits your results too much.
ResultQuery (now with Status)
SELECT c.*
FROM Status INNER JOIN (MaxCommentDateQuery AS md INNER JOIN Comments AS c
ON (md.MaxDate = c.DateCreated) AND (md.Tag = c.Tag))
ON (Status.Criteria2 = c.Criteria2)
AND (Status.Criteria1 = c.Criteria1)
AND (Status.Tag = c.Tag);
Version 2
If you need the criteria associated with the max comment date, use this version:
MaxCommentDateQuery (now with status)
SELECT Tag, MAX(DateCreated) AS MaxDate, Criteria1, Criteria2
FROM Comments
GROUP BY Tag, Criteria1, Criteria2;
ResultQuery (now with Status)
SELECT DISTINCT c.*
FROM Status
INNER JOIN (MaxCommentDateQuery AS md
INNER JOIN Comments AS c
ON (md.Tag = c.Tag) AND (md.MaxDate = c.DateCreated)
AND (c.criteria1 = md.criteria1) AND (c.criteria2 = md.criteria2))
ON (Status.Tag = c.Tag) AND (Status.Criteria1 = c.Criteria1)
AND (Status.Criteria2 = c.Criteria2);