Adding a join to a count() query - sql

I have the following code taken from my previous question here and changed a little.
SELECT *
FROM ES_TOOL
INNER JOIN ES_HARDWARE ON ES_HARDWARE.eshw_ID = ES_TOOL.ESTOOL_HARDWARE
INNER JOIN ES_PAYMENT on ES_payment.espay_id = es_TOOL.estool_payment
LEFT JOIN (
SELECT
tchap.estch_tool, tfacet.estfa_tool,
count(marks.esmrk_value) AmtMarks
FROM ES_MARK marks
left Join ES_TOOL_FACET tfacet ON marks.esmark_tool_facet = tfacet.estfa_id --line added
left Join ES_TOOL_CHAPTER tchap ON marks.esmark_tool_chapter = tchap.estch_id
GROUP BY tchap.estch_tool
) h ON ES_TOOL.estool_id = h.estch_tool
I'm trying to add an additional join in an attempt to get a mark count from "marks" that meet either of the left join "ON" criteria. Without the extra line the query executes, but doesn't count marks that match "facet" criteria. With it I get the following error:
Msg 8120, Level 16, State 1, Line 1
Column 'ES_TOOL_FACET.estfa_tool' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Any help would be appreciated.

The error message means that ES_TOOL_FACET.estfa_tool needs to be included in the Group By.
When you use Group By, all non-aggregated columns must be included in the group by section.

This should be obvious, in your inner query:
SELECT tchap.estch_tool, tfacet.estfa_tool, count(marks.esmrk_value) AmtMarks
FROM ES_MARK marks
left Join ES_TOOL_FACET tfacet ON marks.esmark_tool_facet = tfacet.estfa_id --line added
left Join ES_TOOL_CHAPTER tchap ON marks.esmark_tool_chapter = tchap.estch_id
GROUP BY tchap.estch_tool
you have three selected columns, estch_tool which is in the GROUP BY clause, esmrk_value which is in an aggregate function, and estfa_tool which is neither in the GROUP BY clause nor in an aggregate function.
Your solution should be either:
GROUP BY tchap.estch_tool, tfacet.estfa_tool
AVG(tfacet.estfa_tool) or any aggregate function

There is a syntax error in this query -
SELECT
tchap.estch_tool,
tfacet.estfa_tool,
count(marks.esmrk_value) AmtMarks
FROM ES_MARK marks
left Join ES_TOOL_FACET tfacet ON
marks.esmark_tool_facet = tfacet.estfa_id --line added
left Join ES_TOOL_CHAPTER tchap ON
marks.esmark_tool_chapter = tchap.estch_id
GROUP BY tchap.estch_tool
GROUP BY mandates that any column appearing in SELECT list should either be aggregated or appear in GROUP BY clause.
So put an aggregate function - MIN, MAX, SUM, AVG etc on tfacet.estfa_tool because it does not appear in group by clause or include it there.

My solution comes in two variants. Which one better suits you may depend on which one will yield the better execution plan when tried on your data.
Description of variant #1: (In both cases I am describing only the logic behind the main SELECT's LEFT JOIN subselect, the part that is actually becomes substituted. But the scripts come as complete queries, equivalent to yours):
Pull and UNION ALL the items from both tools tables.
Join the list against the marks table accordingly.
Group the result set by tool items and get the counts.
The query:
SELECT *
FROM ES_TOOL
INNER JOIN ES_HARDWARE ON ES_HARDWARE.eshw_ID = ES_TOOL.ESTOOL_HARDWARE
INNER JOIN ES_PAYMENT on ES_payment.espay_id = es_TOOL.estool_payment
LEFT JOIN (
SELECT
tools.tool,
COUNT(*) AS AmtMarks
FROM (
SELECT 'tchap' AS tbl, estch_id AS id, estch_tool AS tool
FROM ES_TOOL_CHAPTER
UNION ALL
SELECT 'tfacet' AS tbl, estfa_id AS id, estfa_tool AS tool
FROM ES_TOOL_FACET
) tools
INNER JOIN ES_MARK marks
ON tools.tbl = 'tchap' AND tools.id = marks.esmark_tool_chapter
OR tools.tbl = 'tfacet' AND tools.id = marks.esmark_tool_facet
GROUP BY tools.tool
) h ON ES_TOOL.estool_id = h.tool
Variant #2:
Join ES_TOOL_CHAPTER against marks and get all the estch_tool values, including duplicates.
Similarly, join ES_TOOL_FACET against marks and get all the estfa_tool values, with duplicates too.
UNION ALL both sets.
Group the resulting set by tool items and get the counts.
And the query:
SELECT *
FROM ES_TOOL
INNER JOIN ES_HARDWARE ON ES_HARDWARE.eshw_ID = ES_TOOL.ESTOOL_HARDWARE
INNER JOIN ES_PAYMENT on ES_payment.espay_id = es_TOOL.estool_payment
LEFT JOIN (
SELECT
tools.tool,
COUNT(*) AS AmtMarks
FROM (
SELECT estch_tool AS tool
FROM ES_TOOL_CHAPTER tools
INNER JOIN ES_MARK marks ON tools.estch_id = marks.esmark_tool_chapter
UNION ALL
SELECT estfa_tool AS tool
FROM ES_TOOL_FACET tools
INNER JOIN ES_MARK marks ON tools.estfa_id = marks.esmark_tool_facet
) tools
GROUP BY tools.tool
) h ON ES_TOOL.estool_id = h.tool

Related

How to do operations between a column and a subquery

I would like to know how I can do operations between a column and a subquery, what I want to do is add to the field Subtotal what was obtained in the subquery Impuestos, the following is the query that I am using for this case.
Select
RC.PURCHID;
LRC.VALUEMST as 'Subtotal',
isnull((
select sum((CONVERT(float, TD1.taxvalue)/100)*LRC1.VALUEMST ) as a
FROM TAXONITEM TOI1
inner join TAXDATA TD1 ON (TD1.TAXCODE = TOI1.TAXCODE and RC.DATAAREAID = TD1.DATAAREAID)
inner join TRANS LRC1 on (LRC1.VEND = RC.RECID)
WHERE TOI1.TAXITEMGROUP = PL.TAXITEMGROUP and RC.DATAAREAID = TOI1.DATAAREAID
), 0) Impuestos
from VEND RC
inner join VENDTABLE VTB on VTB.ACCOUNTNUM = RC.INVOICEACCOUNT
inner join TRANS LRC on (LRC.VEND = RC.RECID)
inner join PURCHLINE PL on (PL.LINENUMBER =LRC.LINENUM and PL.PURCHID =RC.PURCHID)
where year (RC.DELIVERYDATE) =2021 and RC.PURCHASETYPE =3 order by RC.PURCHID;
Hope someone can give me some guidance when doing operations with subqueries.
A few disjointed facts that may help:
When a SELECT statement returns only one row with one column, you can enclose that statement in parenthesis and use it as a plain value. In your case, let's say that select sum(......= TOI1.DATAAREAID returns 500. Then, your outer select's second column is equivalent to isnull(500,0)
You mention in your question "subquery Impuestos". Keep in mind that, although you indeed used a subquery as we mentioned earlier, by the time it was enclosed in parentheses it is not treated as a subquery (more accurately: derived table), but as a value. Thus, the "Impuestos" is only a column alias at this point
I dislike and avoid subqueries before the from, makes things much harder to read. Here is a solution with apply which will keep your code mostly intact:
Select
RC.PURCHID,
LRC.VALUEMST as 'Subtotal',
isnull(subquery1.a, 0) as Impuestos
from VEND RC
inner join VENDTABLE VTB on VTB.ACCOUNTNUM = RC.INVOICEACCOUNT
inner join TRANS LRC on (LRC.VEND = RC.RECID)
inner join PURCHLINE PL on (PL.LINENUMBER =LRC.LINENUM and PL.PURCHID =RC.PURCHID)
outer apply
(
select sum((CONVERT(float, TD1.taxvalue)/100)*LRC1.VALUEMST ) as a
FROM TAXONITEM TOI1
inner join TAXDATA TD1 ON (TD1.TAXCODE = TOI1.TAXCODE and RC.DATAAREAID = TD1.DATAAREAID)
inner join TRANS LRC1 on (LRC1.VEND = RC.RECID)
WHERE TOI1.TAXITEMGROUP = PL.TAXITEMGROUP and RC.DATAAREAID = TOI1.DATAAREAID
) as subquery1
where year (RC.DELIVERYDATE) =2021 and RC.PURCHASETYPE =3 order by RC.PURCHID;

Oracle SQL - how to NOT SHOW athlete name that apears only once

created a view called winners, it contains the columns: athlete_name,year,medal_won
its basicly athletes that won olympic medal and the year,
it look like that,
data base is in live sql: https://livesql.oracle.com/apex/f?p=590:1000:0
select distinct year,athlete_name,medal
from olym.olym_medals
join olym.olym_athlete_games on olym_athlete_games.id = olym_medals.athlete_game_id
join olym.olym_nations on olym_nations.id = olym_athlete_games.nation_id
join olym.olym_games on olym_games.id = Olym_athlete_games.game_id
join olym.olym_athletes on olym_athletes.id = olym_athlete_games.athlete_id
order by athlete_name
as you can see some name show only once and some names are showing more than once, i want to get rid off all lines of those who show ONLY ONCE, please help me.
thank you!
if i have understand your problem, must group your data,
select year,athlete_name,medal, count(*) "number of Medals"
from olym.olym_medals
join olym.olym_athlete_games on olym_athlete_games.id = olym_medals.athlete_game_id
join olym.olym_nations on olym_nations.id = olym_athlete_games.nation_id
join olym.olym_games on olym_games.id = Olym_athlete_games.game_id
join olym.olym_athletes on olym_athletes.id = olym_athlete_games.athlete_id
group by year,athlete_name,medal;
If I followed you correctly, you can use window functions:
select *
from (
select og.year, oa.athlete_name, om.medal, count(*) over(partition by oa.id) cnt
from olym.olym_medals om
join olym.olym_athlete_games oag on oag.id = om.athlete_game_id
join olym.olym_nations ona on ona.id = oag.nation_id
join olym.olym_games og on og.id = oag.game_id
join olym.olym_athletes oa on oa.id = oag.athlete_id
) t
where cnt > 1
order by athlete_name
Notes:
I am unsure why you were using distinct in the first place, so I removed it (I suspect it is actually not needed)
I added table aliases to shorten the query, and prefixed the columns in the select clause with the table they belong to (you might want to review that) - these are best practices when dealing with multi-table queries
Use GROUP BY and HAVING COUNT(*) > 1:
SELECT year,
athlete_name,
medal
FROM olym.olym_medals
INNER JOIN olym.olym_athlete_games
ON olym_athlete_games.id = olym_medals.athlete_game_id
INNER JOIN olym.olym_nations
ON olym_nations.id = olym_athlete_games.nation_id
INNER JOIN olym.olym_games
ON olym_games.id = Olym_athlete_games.game_id
INNER JOIN olym.olym_athletes
ON olym_athletes.id = olym_athlete_games.athlete_id
GROUP BY
year,
athlete_name,
medal
HAVING COUNT(*) > 1
ORDER BY athlete_name

Rows which appeared only once in query results

I want to have rows which appeared in 25th of February and did not appear in 6th of March. I've tried to do such query:
SELECT favfd.day, dv.title, array_to_string(array_agg(distinct "host_name"), ',') AS affected_hosts , htmltoText(dsol.fix),
count(dv.title) FROM fact_asset_vulnerability_finding_date favfd
INNER JOIN dim_vulnerability dv USING(vulnerability_id)
INNER JOIN dim_asset USING(asset_id)
INNER JOIN dim_vulnerability_solution USING(vulnerability_id)
INNER JOIN dim_solution dsol USING(solution_id)
INNER JOIN dim_solution_highest_supercedence dshs USING (solution_id)
WHERE (favfd.day='2018-02-25' OR favfd.day='2018-03-06') AND
dsol.solution_type='PATCH' AND dshs.solution_id=dshs.superceding_solution_id
GROUP BY favfd.day, dv.title, host_name, dsol.fix
ORDER BY favfd.day, dv.title
which gave me following results:
results
I've read that I need to add something like "HAVING COUNT(*)=1" but like you can see in query results my count columns looks quite weird. Here is my results with that line added:
results with having
Can you advice me what I am doing wrong?
One way is to use a HAVING clause to assert your date criteria:
SELECT
dv.title,
array_to_string(array_agg(distinct "host_name"), ',') AS affected_hosts,
htmltoText(dsol.fix),
count(dv.title)
FROM fact_asset_vulnerability_finding_date favfd
INNER JOIN dim_vulnerability dv USING(vulnerability_id)
INNER JOIN dim_asset USING(asset_id)
INNER JOIN dim_vulnerability_solution USING(vulnerability_id)
INNER JOIN dim_solution dsol USING(solution_id)
INNER JOIN dim_solution_highest_supercedence dshs USING (solution_id)
WHERE
dsol.solution_type = 'PATCH' AND
dshs.solution_id = dshs.superceding_solution_id
GROUP BY dv.title, dsol.fix
HAVING
SUM(CASE WHEN favfd.day = '2018-02-25' THEN 1 ELSE 0 END) > 0 AND
SUM(CASE WHEN favfd.day = '2018-03-06' THEN 1 ELSE 0 END) = 0
ORDER BY favfd.day, dv.title
The only major changes I made were to remove host_name from the GROUP BY clause, because you use this column in aggregate, in the select clause. And I added the HAVING clause to check your logic.
The change you should make is to replace your implicit join syntax with explicit joins. Putting join criteria into the WHERE clause is considered bad practice nowadays.

Query to return SINGLE DISTINCT row

I have the query below working, the thing is I need to only list each unique "VolumeSerialNumber0" once. There's no shortage of questions and approaches to this problem on SO but they suggest using subqueries and group by clause, but when I try to do that I get an error "columnname is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
I feel like it has to be close I'm just not getting the magical syntax perfectly correct.
SELECT
dbo.v_R_System.Netbios_Name0,
dbo.v_GS_LOGICAL_DISK.TimeStamp,
dbo.v_GS_LOGICAL_DISK.Description0,
dbo.v_GS_LOGICAL_DISK.DeviceID0,
dbo.v_GS_LOGICAL_DISK.DriveType0,
dbo.v_GS_LOGICAL_DISK.Name0,
dbo.v_GS_LOGICAL_DISK.SystemName0,
dbo.v_GS_LOGICAL_DISK.VolumeName0,
dbo.v_GS_LOGICAL_DISK.VolumeSerialNumber0,
dbo.v_GS_PARTITION.Size0,
dbo.v_GS_LOGICAL_DISK.FileSystem0
FROM
dbo.v_R_System
INNER JOIN dbo.v_GS_LOGICAL_DISK
ON dbo.v_R_System.ResourceID = dbo.v_GS_LOGICAL_DISK.ResourceID
INNER JOIN dbo.v_GS_PARTITION
ON dbo.v_GS_LOGICAL_DISK.ResourceID = dbo.v_GS_PARTITION.ResourceID
SELECT
MAX(S.Netbios_Name0),
MAX(L.TimeStamp),
MAX(L.Description0),
MAX(L.DeviceID0),
MAX(L.DriveType0),
MAX(L.Name0),
MAX(L.SystemName0),
MAX(L.VolumeName0),
L.VolumeSerialNumber0,
MAX(P.Size0),
MAX(L.FileSystem0)
FROM
dbo.v_R_System S
INNER JOIN dbo.v_GS_LOGICAL_DISK L
ON S.ResourceID = L.ResourceID
INNER JOIN dbo.v_GS_PARTITION P
ON L.ResourceID = P.ResourceID
GROUP BY
L.VolumeSerialNumber0

Subquery with multiple joins involved

Still trying to get used to writing queries and I've ran into a problem.
Select count(region)
where (regionTable.A=1) in
(
select jxn.id, count(jxn.id) as counts, regionTable.A
from jxn inner join
V on jxn.id = V.id inner join
regionTable on v.regionID = regionTable.regionID
group by jxn.id, regionTable.A
)
The inner query gives an ID number in one column, the amount of times they appear in the table, and then a bit attribute if they are in region A. The outer query works but the error I get is incorrect syntax near the keyword IN. Of the inner query, I would like a number of how many of them are in region A
You must specify table name in query before where
Select count(region)
from table
where (regionTable.A=1) in
And you must choose one of them.
where regionTable.A = 1
or
where regionTable.A in (..)
Your query has several syntax errors. Based on your comments, I think there is no need for a subquery and you want this:
select jxn.id, count(jxn.id) as counts, regionTable.A
from jxn inner join
V on jxn.id = V.id inner join
regionTable on v.regionID = regionTable.regionID
where regionTable.A = 1
group by jxn.id, regionTable.A
which can be further simplified to:
select jxn.id, count(jxn.id) as counts
, 1 as A --- you can even omit this line
from jxn inner join
V on jxn.id = V.id inner join
regionTable on v.regionID = regionTable.regionID
where regionTable.A = 1
group by jxn.id
You are getting the error because of this line:
where (regionTable.A=1)
You cannot specify a condition in a where in clause, it should only be column name
Something like this may be what you want:
SELECT COUNT(*)
FROM
(
select jxn.id, count(jxn.id) as counts, regionTable.A
from
jxn inner join
V on jxn.id = V.id inner join
regionTable on v.regionID = regionTable.regionID
group by jxn.id, regionTable.A
) sq
WHERE sq.a = 1