Oracle SQL STATS_MODE with two tables - sql

I have the tables listed in the image. I need to display the institution and loan type of the most used financing plan in the sale_financing table. I have tried using the stats_mode function but I have not been able to get it to work. I am supposed to only display the most used financing plan and I keep getting 3 showing up. Here is an image of the tables.
My image may not work so here are the tables:
financing_plan
id
institution
loan_type
sale_financings
id
plan_id ------> foreign key linking to - financing_plan.id
I have tried several different ways in the Query Builder and I cannot get it to work.
Here is one :
SELECT
financing_plans.institution,
financing_plans.loan_type,
STATS_MODE(sale_financings.plan_id) AS stats_mode_plan_id
FROM
financing_plans
INNER JOIN sale_financings ON financing_plans.id =
sale_financings.plan_id
GROUP BY
financing_plans.institution,
financing_plans.loan_type
Another:
SELECT
financing_plans.institution,
financing_plans.loan_type,
STATS_MODE(sale_financings.plan_id) AS stats_mode_plan_id
FROM
financing_plans
INNER JOIN sale_financings ON financing_plans.id =
sale_financings.plan_id
GROUP BY
financing_plans.institution,
financing_plans.loan_type
HAVING
STATS_MODE(sale_financings.plan_id) = sale_financings.plan_id

Count the use of each plan_id, then rank these (using dense_rank()) by the count (descending order) allows "top" and "equal top" to be shown.
select
fp.institution, fp.loan_type, s.plan_count
from financing_plan fp
inner join (
select plan_id, plan_count, dense_rank() over(order by plan_count DESC) as rnk
from (
select plan_id, count(id) plan_count
from sale_financings
Group by plan_id
)
) s on fp.id = s.plan_id and s.rnk = 1
order by
fp.institution, fp.loan_type
;

Related

Inner join in my query seems to not be recognized

I have a little question about a problem with a SQL query. I've written a query to get some records and the number of occurences of those records. The problem is that I'd like to join two tables so I can get some values instead of foreign keys. This is my actual query (which I think is wrong because there is no joins in the result) :
SELECT date, heure_debut, heure_fin, Event_id,
horaire_id, local_id, enseignant_id, COUNT(*) doublons
FROM `reservations`
INNER JOIN `events` ON `events`.`id` = `reservations`.`Event_id`
INNER JOIN `couleurs` ON `couleurs`.`id` = `events`.`couleur_id`
GROUP BY date,
heure_debut,
heure_fin,
Event_id,
horaire_id,
local_id,
enseignant_id
HAVING COUNT(*) > 1 OR COUNT(*) = 1
Thank you for your answers.
Sort of a wild guess based on the OP's description of his intent.
Assuming that table events has a column titre that is an AK, the following modifications to the original query might mirror this intent. Instead of the event id it shows its name:
SELECT date, heure_debut, heure_fin, `events`.titre,
horaire_id, local_id, enseignant_id, COUNT(*) doublons
FROM `reservations`
INNER JOIN `events` ON `events`.`id` = `reservations`.`Event_id`
INNER JOIN `couleurs` ON `couleurs`.`id` = `events`.`couleur_id`
GROUP BY date,
heure_debut,
heure_fin,
`events`.titre,
horaire_id,
local_id,
enseignant_id
HAVING COUNT(*) > 0
;

SQL Query with row_number() not returning expected output

my goal is to write a query that should return the cities which produced the highest avg. sales for each item-category.
This is the expected output:
item_category|city
books |los_angeles
toys |austin
electronics |san_fransisco
My 3 table schemas look like this:
users
user_id|city
sales
user_id|item_id|sales_amt
items
item_id|item_category
These are further notes to consider:
1. sales_amt is the only column that may have Null values. if no users have placed a sale for a particular item-category (no rows in sales with a non-Null sales_amt), then the city name should be Null.
2. only 1 row per each distinct item. It more than 1 city qualify, then pick the first one alphabetically.
The attempt I took looks like this but it does not produce the right output:
select a.item_category,a.city from (
select
i.item_category,
u.city,
row_number() over (partition by i.item_category,u.city order by avg(s.sales_amt) desc)rk
from sales s
join users u on s.user_id=u.user_id
join items i on i.item_id=s.item_id
group by i.item_category,u.city)a
where a.rk=1
My output does not return the Null cased for sales_amt. Also, I get non-unique rows. Therefore, I am very nervous I am not properly incorporating the 2 notes.
I hope someone can help.
my goal is to write a query that should return the cities which produced the highest avg. sales for each item-category.
This can be calculated using aggregation and window functions:
select ic.*
from (select i.item_category, u.city,
row_number() over(partition by u.item_category order by avg(s.sales_amt) desc, u.city) as seqnum
from users u join
sales s
on s.user_id = u.user_id join
items i
on i.item_id = s.item_id
group by i.item_category, u.city
) ic
where seqnum = 1;
Your question explicitly says "average" which is why this uses avg(). However, I suspect that you really want the sum in each city, which would be sum().
Notes:
You want one row so row_number() instead of rank().
You need sales to calculate the average, so join, instead of left join.
You want one row per item_category, so that is used for partitioning.
Aaaand my take on it is a mix of GMB and Gordon's advices; GMB points out that left joins are needed but I think his starting table, partition and choice of rank() is wrong (his query cannot generate null city names as requested, and could generate duplicates tied on same avg), and Gordon picked up on things like ordering by city on a tied avg which GMB did not but missed the "if no sales of any items in category X put null for the city" requirement. Both guys left cancelled orders floating round the system which introduces errors:
select *
from (
select
i.item_category,
u.city,
row_number() over(partition by i.item_category order by avg(s.sales_amt) desc, u.city asc) rn
from items i
left join (select * from sales where sale_amt is not null) s on i.item_id = s.item_id
left join users u on s.user_id = u.user_id
group by i.item_category, u.city
) t
where rn = 1
We start from itemcategory so that categories having no sales get nulls for their sale amount and city.
We also need to consider that any sales that didn't fulfil will have null in their amount and we exclude these with a subquery otherwise they will link through to users giving a false positive - even though the avg will calculate as null for a category that only has cancelled orders, the city will still show when it should not). I could also have done this with a and sales_amt is not null predicate in the join but I think this way is clearer. This should not be done with a predicate in the where clause because that will eliminate the sale-less categories we are trying to preserve
Row number is used on avg but with city name to break any ties. It's a simpler function than rank and cannot generate duplicate values
Finally we pull the rn 1s to get the top averaging cities
I think you want left joins starting from users in the inner query to preserve cities without sales.
As for the ranking: if you want one record per city, then do not put other columns that city in the partition (your current partition gives you one record per city and per category, which is not what you want).
Consider:
select *
from (
select
i.item_category,
u.city,
rank() over(partition by u.city order by avg(s.sales_amt) desc) rk
from users u
left join sales s on s.user_id = u.user_id
left join items i on i.item_id = s.item_id
group by i.item_category, u.city
) t
where rk = 1

Sub query to count number of time an id appears in another table

Using SQL Server 2012. I have a table called deals that contains a primary key called deal_id along with 10 other fields. I also have a table called deals_country that contain a foreign key called deal_id.
It's possible that a record in deals contains numerous records in deals country. What I want to do is to count the number of times every deal_id from deals appears in deals_country?
Below is what I have tried without success.
select MA_DEALS.*, MA_DEALS_COUNTRY.mycount
from MA_DEALS cross apply
(
select count(MA_DEALS_COUNTRY.deal_id) as mycount
from MA_DEALS_COUNTRY
group by MA_DEALS_COUNTRY.deal_id
) MA_DEALS_COUNTRY
order by MA_DEALS.deal_id
Although you can use CROSS APPLY for this, I would start with the basic JOIN and GROUP BY query instead:
select MA_DEALS.*, dc.mycount
from MA_DEALS d left join
(select dc.deal_id, count(dc.deal_id) as mycount
from MA_DEALS_COUNTRY dc
group by dc.deal_id
) dc
on d.deal_id = dc.deal_id
order by d.deal_id;
Try this:
SELECT D.*,
DC.N
FROM MA_DEALS D
LEFT JOIN ( SELECT deal_id, COUNT(*) N
FROM MA_DEALS_COUNTRY
GROUP BY deal_id) DC
ON D.deal_id = DC.deal_id

Select last record out of grouped records

i have this code and i want someone to help me to change it to a grouped query which orders froms below.
SELECT *
FROM dbo.users_pics INNER JOIN profile
ON users_pics.email = profile.email
Left Join photo_comment
On users_pics.u_pic_id = photo_comment.pic_id
WHERE users_pics.wardrobe = MMColParam
ORDER BY u_pic_id asc
what i mean is i have grouped of records which i want to select one per record only from beneath. for example if i have 10 records of the name "John" i want to select the last "John" out of the 10 and then the rest also follows
I'm going to presume that your users table contains a single user, and each user has a single profile, and your photo_comment table can contain multiple comments.
Depending on your RDBMS, you can do this a number of ways. Row_Number can often be a quick way of doing this if you're using a database which supports window functions such as SQL Server or Oracle.
A generic solution to this is to join the table back to itself using the MAX aggregate. This is dependent on having a field to determine which record is the max. Generally speaking, that would be an identity/auto number field or a time stamp field.
Here is the basic concept using photo_comment_id as your determining column:
SELECT *
FROM dbo.users_pics INNER JOIN profile
ON users_pics.email = profile.email
LEFT Join (
SELECT pic_id, MAX(photo_comment_id) max_photo_comment_id
FROM max_photo_comment
GROUP BY pic_id
) max_photo_comment On users_pics.u_pic_id = max_photo_comment.pic_id
LEFT Join photo_comment On
max_photo_comment.pic_id = photo_comment.pic_id AND
max_photo_comment.max_photo_comment_id = photo_comment.photo_comment_id
WHERE users_pics.wardrobe = MMColParam
ORDER BY u_pic_id asc
If your database supports ROW_NUMBER, then you can do this as well (still using the photo_comment_id field):
SELECT *
FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY photo_comment.pic_id
ORDER BY photo_comment.photo_comment_id DESC) rn
FROM dbo.users_pics INNER JOIN profile
ON users_pics.email = profile.email
LEFT JOIN photo_comment
ON users_pics.u_pic_id = photo_comment.pic_id
WHERE users_pics.wardrobe = MMColParam
) t
WHERE rn = 1
ORDER BY u_pic_id asc

Find MAX with JOIN where Field also shows up in another Table

I have 3 tables: Master, Paper and iCodes. For a certain set of Master.Ref's, I need to find Max(Paper.Date), where the Paper.Code is also in the iCodes table (i.e., Paper.Code is a type of iCode). Master is joined to Paper by the File field.
EDIT:
I only need the Max(Paper.Date) its corresponding Code; I do not need all of the Codes.
I wrote the following but it is very slow. I have a few hundred ref #'s to look for. What is a better way to do this?
SELECT Master.Ref,
Paper.Code,
mp.MaxDate
FROM ( SELECT p.File ,
MAX(p.Date) AS MaxDate ,
FROM Paper AS p
LEFT JOIN Master AS m ON p.File = m.File
WHERE m.Ref IN ('ref1', 'ref2', 'ref3', 'ref4', 'ref5', 'ref6'... )
AND p.Code IN ( SELECT DISTINCT i.iCode
FROM iCodes AS i
)
GROUP BY p.File
) AS mp
LEFT JOIN Master ON mp.File = Master.File
LEFT JOIN Paper ON Master.File = Paper.File
AND mp.MaxDate = Paper.Date
WHERE Paper.Code IN ( SELECT DISTINCT iCodes.iCode
FROM iCodes
)
Does this do what you want?
SELECT m.Ref, p.Code, max(p.date)
FROM Master m LEFT JOIN
Paper
ON m.File = p.File
WHERE p.Code IN (SELECT DISTINCT iCodes.iCode FROM iCodes) and
m.Ref IN ('ref1','ref2','ref3','ref4','ref5','ref6'...)
GROUP BY m.Ref, p.Code;
EDIT:
To get the code on the max date, then use window functions:
select ref, code, date
from (SELECT m.Ref, p.Code, p.date
row_number() over (partition by m.Ref order by p.date desc) as seqnum
FROM Master m LEFT JOIN
Paper
ON m.File = p.File
WHERE p.Code IN (SELECT DISTINCT iCodes.iCode FROM iCodes) and
m.Ref IN ('ref1','ref2','ref3','ref4','ref5','ref6'...)
) mp
where seqnum = 1;
The function row_number() assigns a sequential number starting at 1 to a group of rows. The groups are defined by the partition by clause, so in this case everything with the same m.Ref value would be in a single group. Within the group, rows are assigned the number based on the order by clause. So, the one with the biggest date gets the value of 1. That is the row you want.