Cannot figure out writing a compound SQLquery to the Oracle database - sql

Please help me sort out the request:
Develop a query to calculate the number of news, written by each author and the most popular tag, referred to author news. All these information must be output in one single result set.
I wrote the first part of the request, it displays the amount of news for each author:
SELECT news_author.author_id AS "Author ID", COUNT(*) AS "Amount of news"
FROM news
JOIN news_author ON id = news_author.news_id
JOIN news_tag ON id = news_tag.news_id
GROUP BY news_author.author_id
ORDER BY news_author.author_id;
Please tell me how to make a request for the most popular tag, referred to author news and combine these two samples into one result set.
You can use standard ANSI SQL features or Oracle SQL bonuses.
The table scheme is attached.

The most popular tag has a name in statistics, called the mode. And, Oracle has an aggregation function stats_mode() to calculate it. So, you can use:
SELECT na.author_id AS "Author ID",
COUNT(DISTINCT n.id) AS num_news,
STATS_MODE(nt.tag_id)
FROM news n JOIN
news_author na
ON n.id = na.news_id JOIN
news_tag nt
ON n.id = nt.news_id
GROUP BY na.author_id
ORDER BY na.author_id;

Related

Going from SQLite to SAP ASE/SQL Server need some assistance on query rewrite

I wrote the following query in SQLite, which works fine, but have found out the office utilizes SAP ASE (Sybase SQL Server) and it does not display the same result there.
select
dm04_maf.mcn,
dm04_maf.wc_cd,
dm04_maf.buno_serno,
max(dm12_maf_note.maf_note) as Last_Note,
dm12_maf_note.note_dttm as Time_of_Note,
dm12_maf_note.orignr
from
dm04_maf
left join
dm12_maf_note on dm04_maf.mcn = dm12_maf_note.mcn
where dm04_maf.ty_maf_cd = 'TD'
group by dm04_maf.mcn
I believe it is not performing group by correctly as it isn't giving me the last note for each mcn (primary key) it is giving me every note for each mcn.
Any guidance for this would be appreciated.
An ANSI compliant group by query will have all non-aggregate columns (from the select/projection list) also in the group by clause. While many RDBMSs will allow non-ANSI compliant group by queries (like in this question), how each RDBMS chooses to process said non-ANSI compliant group by query is up for grabs (ie, there is no guarantee of getting the same result across different RDBMSs).
Some assumptions:
OP mentions wanting to display just the 'last note'; for now we'll assume that max(maf_note) is sufficient to determine the 'last note' for a given mcn value
the other non-aggregate columns (eg, wc_cd, buno_serno, note_dttm and orignr) should come from the same row that produces last note = max(maf_note)`
Since SAP (Sybase) ASE does not support windows functions nor ROW_NUMBER(), one idea would be to use a sub-query to find the 'last note' and then join this into the main query to pull the rest of the desired values, eg:
select dm1.mcn,
dm1.wc_cd,
dm1.buno_serno,
dt.Last_Note,
dmn1.note_dttm as Time_of_Note,
dmn1.orignr
from dm04_maf dm1
left
join dm12_maf_note dmn1
on dm1.mcn = dmn1.mcn
join (select dm2.mcn,
max(dmn2.maf_note) as Last_Note
from dm04_maf dm2
join dm12_maf_note dmn2
on dm2.mcn = dmn2.mcn
where dm2.ty_maf_cd = 'TD'
group by dm2.mcn
) dt
on dm1.mcn = dt.mcn
and dmn1.maf_note = dt.Last_Note
where dm1.ty_maf_cd = 'TD'
NOTES:
the extra dm1.ty_maf_cd = 'TD' is likely redundant; will leave it up to the OP to decide on whether to keep or remove
(obviously) may need to come back and tweak based on validity of the assumptions and/or updates to the question
With ROW_NUMBER() window function:
select t.mcn, t.wc_cd, t.buno_serno,
t.maf_note as Last_Note,
t.note_dttm as Time_of_Note,
t.orignr
from (
select d04.mcn, d04.wc_cd, d04.buno_serno,
d12.maf_note, d12.note_dttm, d12.orignr,
row_number() over (partition by d04.mcn order by d12.maf_note desc) rn
from dm04_maf d04 left join dm12_maf_note d12
on d04.mcn = d12.mcn
where d04.ty_maf_cd = 'TD'
) t
where t.rn = 1

strange Hibernate-generated SQL with OVER() function and ORDER BY ORDER ON

I'm having a problem with a slight ordering anomaly in a legacy web application, and figured I'd start with the back-end SQL query generated by Hibernate with DB2Dialect:
FROM (SELECT inner2_.*,
ROWNUMBER()
OVER(
ORDER BY ORDER OF inner2_) AS rownumber_
FROM (SELECT this_.sohn AS SOHN1_15_11_,
this_.aslc AS ASLC2_15_11_,
this_.cc AS CC3_15_11_,
bb1_.sbn AS SBN1_2_0_,
bb1_.abc AS ABC3_4_5_,
mh2_.smhn AS SMHN1_9_1_,
mh2_.sabc AS SABC3_4_6_,
og8_.sogn AS SOGN1_11_2_,
og8_.sogo AS SOGO3_4_7_,
oc9_.socn AS SOCN_1_13_3_,
oc9_.soco AS SOCO_3_4_8_
FROM ott.oh this_
INNER JOIN ott.bb1_
ON this_.sbn = bb1_.sbn
INNER JOIN ott.mh2_
ON this_.smhn = mh2_.smhn
LEFT OUTER JOIN ott.og og8_
ON this_.sogn = og8_.sogn
LEFT OUTER JOIN ott.oc oc9_
ON this_.socn = oc9_.socn
WHERE ( 1 = 1 )
AND bb1_.sbn = ?
AND mh2_.smhn = ?
FETCH first 200 ROWS only) AS inner2_) AS inner1_
WHERE rownumber_ > 190
ORDER BY rownumber_
What does this query do? I am especially curious about OVER(), which isn't coming up when I google for such a SQL function (but it is an MDX function?).
This query functions in the application to grab the last page of a paginated list that is ordered by a field that doesn't even appear in the query. The query to populate the first page on initial load is different, and its generated SQL does ORDER BY the desired field.
So to get through this I need to understand how the query functions. Takers?
OVER() is part of so called OLAP functions - a good desrciption can be found in the DB2 SQL Cookbook - i.e. available here:
http://www.ids-system.de/images/Downloads/DB2V97CK.PDF
It is a group of really useful functions.
Also good additional stuff
http://www.ibm.com/developerworks/data/library/techarticle/dm-0401kuznetsov/

Trouble with integrating COUNT function in SQL query using Oracle

I am trying to self educate myself in SQL in order to better use databases at work. For this purpose I am using Oracle Application Express. This if my first time using the COUNT function and I am having some difficulties integrating it within my query. I have done a great deal of research and read quite a bit of literature but I just can't seem to get it right.
My goal is to display the channel_name and channel_number columns (from the channel table) for each channel along with a count of the number of customers that have that channel as a favorite channel (survey_result column from the survey table). Please see below for code:
SELECT channel.channel_number,
channel.channel_name,
survey.survey_result,
count(SELECT survey.survey_result FROM survey)
From Channel, survey
WHERE survey.channel_number = channel.channel_number
Currently I am getting the error message:
ORA-00936: missing expression.
Try this:
Below query gives you only those channels which have minimum 1 customer.
SELECT C.channel_number, C.channel_name, COUNT(S.survey_result) NoOfCustomers
FROM Channel C
INNER JOIN survey S ON S.channel_number = C.channel_number
GROUP BY C.channel_number, C.channel_name;
And below query gives you all channels whether it has customer or not.
SELECT C.channel_number, C.channel_name, COUNT(S.survey_result) NoOfCustomers
FROM Channel C
LEFT JOIN survey S ON S.channel_number = C.channel_number
GROUP BY C.channel_number, C.channel_name;
Either of these may work for you
SELECT channel.channel_number,
channel.channel_name,
count(survey.survey_result)
From Channel, survey
WHERE survey.channel_number = channel.channel_number
GROUP BY
channel.channel_number,
channel.channel_name
or
SELECT channel.channel_number,
channel.channel_name,
survey.survey_result,
(SELECT count(survey_result) FROM survey)
From Channel, survey
WHERE survey.channel_number = channel.channel_number
count is an aggregate function thus you should have a group by on channel.channel_number and channel.channel_name. then just use count(survey.survey_result) instead of count(SELECT survey.survey_result FROM survey). Madhivanan's and Saharsh Shah's answers look good to me. including this answer to explain why.

Oracle SQL get column value associated with max(count())

I have the following query. What results is two coloums, oakid and max(count(rating)). What I want is to have two columns, but instead of actually having the max(count(rating)), I want the rating associated with the max(count(rating)). How can I modify my query to give me this?
select oakid,
(select max(count(rating))
from climbs, routes
where climbs.routename = routes.name
and climbs.climberid = oakid group by routes.rating)
as skill
from climbers;
For example, if I have ratings "hard, hard, easy, easy, easy, medium", the max(count(rating)) will show "3", since there are 3 "easy" ratings, but I want it to show "easy", not "3".
It sounds as though you want the statistical mode (most frequently occurring) rating for each oakid or climberid. Oracle has a handy function to do this for us called STATS_MODE:
SELECT c.climberid AS oakid, STATS_MODE(r.rating) AS skill
FROM climbs c
INNER JOIN routes r ON (c.routename = r.name)
GROUP BY c.climberid;
try the following:
select oakid,
(select rating
from climbs, routes
where climbs.routename = routes.name
and climbs.climberid = oakid
group by routes.rating
having max(count(rating)) = count(rating))
as skill
from climbers

SUM(a*b) not working

I have a PHP page running in postgres. I have 3 tables - workorders, wo_parts and part2vendor. I am trying to multiply 2 table column row datas together, ie wo_parts has a field called qty and part2vendor has a field called cost. These 2 are joined by wo_parts.pn and part2vendor.pn. I have created a query like this:
$scoreCostQuery = "SELECT SUM(part2vendor.cost*wo_parts.qty) as total_score
FROM part2vendor
INNER JOIN wo_parts
ON (wo_parts.pn=part2vendor.pn)
WHERE workorder=$workorder";
But if I add the costs of the parts multiplied by the qauntities supplied, it adds to a different number than what the script is doing. Help....I am new to this but if someone can show me in SQL I can modify it for postgres. Thanks
Without seeing example data, there's no way for us to know why you're query totals are coming out differently that when you do the math by hand. It could be a bad join, so you are getting more/less records than you expected. It's also possible that your calculations are off. Pick an example with the smallest number of associated records & compare.
My suggestion is to add a GROUP BY to the query:
SELECT SUM(p.cost * wp.qty) as total_score
FROM part2vendor p
JOIN wo_parts wp ON wp.pn = p.pn
WHERE workorder = $workorder
GROUP BY workorder
FYI: MySQL was designed to allow flexibility in the GROUP BY, while no other db I've used does - it's a source of numerous questions on SO "why does this work in MySQL when it doesn't work on db x...".
To Check that your Quantities are correct:
SELECT wp.qty,
p.cost
FROM WO_PARTS wp
JOIN PART2VENDOR p ON p.pn = wp.pn
WHERE p.workorder = $workorder
Check that the numbers are correct for a given order.
You could try a sub-query instead.
(Note, I don't have a Postgres installation to test this on so consider this more like pseudo code than a working example... It does work in MySQL tho)
SELECT
SUM(p.`score`) AS 'total_score'
FROM part2vendor AS p2v
INNER JOIN (
SELECT pn, cost * qty AS `score`
FROM wo_parts
) AS p
ON p.pn = p2v.pn
WHERE p2n.workorder=$workorder"
In the question, you say the cost column is in part2vendor, but in the query you reference wo_parts.cost. If the wo_parts table has its own cost column, that's the source of the problem.