how to get only one value from subquery - sql

select users.name
from(
select users.name, pay.uid,
RANK() OVER(ORDER BY count(pay.uid) DESC) AS ranking
from pay, users
where users.uid = pay.uid
group by users.uid)
where ranking = 1
i only want to get users.name output not name,uid,rank.

SELECT sub.name
FROM (SELECT users.name, pay.uid,
RANK() OVER(ORDER BY count(pay.uid) DESC) AS ranking
FROM pay
INNER JOIN users
on users.uid = pay.uid
GROUP BY users.uid) sub
WHERE ranking = 1
alias the inline view (sub)
change alias on outer select to sub instead of pay
The outer query has no knowledge of the tables inside thus the sub alias.
avoid using , for joins that's a 1980s technique the newer standars are to use joins (inner, outer (left, right full), cross etc.)

If I read your problem
You can use this query:
select table.name
from(select users.name, loans.uid, RANK() OVER(ORDER BY count(loans.uid) DESC) AS ranking from loans left join users on users.uid = loans.uid group by users.uid) as 'table'
where ranking = 1

Related

Select second most recent date from inner join

I have this query :
SELECT
companies.display_name, companies.pay_schedule_id,
pay_schedule_periods.schedule_id,
pay_schedule_periods.created_at
FROM
companies
INNER JOIN
pay_schedule_periods ON pay_schedule_id = pay_schedule_periods.schedule_id
ORDER BY
companies.display_name, pay_schedule_periods.created_at DESC;
I get this result :
How can I select only the second most recent created_at date from each unique display_name ?
You could use row_number to assign a sequence to your dates and apply this before joining, then include as part of your join criteria, such as:
select c.display_name, c.pay_schedule_id, psp.schedule_id, psp.created_at
from companies c
join (
select pay_schedule_id, created_at,
Row_Number() over(partition by pay_schedule_id order by created_at desc) rn
from pay_schedule_periods
)psp on psp.schedule_id = c.pay_schedule_id and rn = 2
order by c.display_name, psp.created_at desc;
You could also apply this using a lateral join which would simplify further.

SQL Problem i don't know how to change by queries on inner join

I have a problem
I have 2, same tables. My select should show this two columns and the last one "sup_cr" should be counted.
sup_id, sup_name, sup_cr.
This is my query without inner join. But i don't know how to use a inner join in this query.
SELECT sup.sup_id,
sup.sup_name_en AS name,
(SELECT COUNT (sup_cr.sup_id_from)
FROM t_sup_supplier AS sup_cr
WHERE sup_cr.sup_id_from = sup.sup_id ) AS cr_numbers
FROM t_sup_supplier AS sup
Sorry for my english.
You don't need a join or aggregation. You can use a window function:
SELECT sup.sup_id, sup.sup_name_en AS name,
COUNT(*) OVER (PARTITION BY sup_id) AS cr_numbers
FROM t_sup_supplier sup;
You could rephrase this a join using:
SELECT
sup.sup_id,
sup.sup_name_en AS name,
COALESCE(sup_cr.cnt, 0) AS cr_numbers
FROM t_sup_supplier
LEFT JOIN
(
SELECT sup_id_from, COUNT(*) AS cnt
FROM t_sup_supplier
GROUP BY sup_id_from
) sup_cr
ON sup_cr.sup_id_from = sup.sup_id;

Get Min date as condition

I have a table that contains invoices for all phone numbers, and each number has several invoices, I want to display only the first invoice for precise number but i don't really know how get only first invoice , this is my query
SELECT
b.contrno
a.AR_INVDATE
FROM P_STG_TABS.IVM_INVOICE_RECORD a
INNER JOIN P_EDW_TMP.invoice b
ON b.contrno=a.contrno
WHERE a.AR_INVDATE< (SELECT AR_INVDATE FROM P_STG_TABS.IVM_INVOICE_RECORD WHERE contrno=b.contrno )
Teradata supports a QUALIFY clause to filter the result of an OLAP-function (similar to HAVING after GROUP BY), which greatly simplifies Tim Biegeleisens's answer:
SELECT *
FROM P_STG_TABS.IVM_INVOICE_RECORD a
INNER JOIN P_EDW_TMP.invoice b
ON b.contrno = a.contrno
QUALIFY
ROW_NUMBER()
OVER (PARTITION BY b.contrno
ORDER BY a.AR_INVDATE) = 1
Additionally you can apply the ROW_NUMBER before the join (might be more efficient depending on additional conditions):
SELECT *
FROM
( SELECT *
FROM P_STG_TABS.IVM_INVOICE_RECORD a
QUALIFY
ROW_NUMBER()
OVER (PARTITION BY b.contrno
ORDER BY a.AR_INVDATE) = 1
) AS a
INNER JOIN P_EDW_TMP.invoice b
ON b.contrno = a.contrno
Use ROW_NUMBER():
SELECT
t.contrno,
t.AR_INVDATE
FROM
(
SELECT
b.contrno,
a.AR_INVDATE,
ROW_NUMBER() OVER (PARTITION BY b.contrno ORDER BY a.AR_INVDATE) rn
FROM P_STG_TABS.IVM_INVOICE_RECORD a
INNER JOIN P_EDW_TMP.invoice b
ON b.contrno = a.contrno
) t
WHERE t.rn = 1;
If you are worried about ties, and you want to display all ties, then you can replace ROW_NUMBER with either RANK or DENSE_RANK.
If I correctly understand, then one way is to use group by with min(a.AR_INVDATE):
SELECT
b.contrno,
min(a.AR_INVDATE)
FROM P_STG_TABS.IVM_INVOICE_RECORD a
INNER JOIN P_EDW_TMP.invoice b
ON b.contrno=a.contrno
group by b.contrno

postgres join max date

I need to construct a join that will give me the most recent price for each product. I vastly simplified the table structures for the purpose of the example, and each table row counts will be in the millions. My previous stabs at this have not exactly been very effecient.
In PostgreSQL, you could try DISTINCT ON to only get the first row per product id in descending create_date order;
SELECT DISTINCT ON (products.id) products.*, prices.*
FROM products
JOIN prices
ON products.id = prices.product_id
ORDER BY products.id, create_date DESC
(of course, except for illustrative purposes, you should of course select the exact columns you need)
The simplest way to do it is using the row_number function.
SELECT
p.name,
t.amount AS latest_price
FROM (
SELECT
p.*,
row_number() OVER (PARTITION BY product_id ORDER BY create_date DESC) AS rn
FROM
prices p) t
JOIN products p ON p.id = t.product_id
WHERE
rn = 1
While the DISTINCT ON answer worked for my instance, I found there's a faster way for me to get what I need.
SELECT
DISTINCT ON(u.id) u.id,
(CAST(data AS JSON) ->> 'Finished') AS Finished,
ee.post_value
FROM
users_user u
JOIN events_event ee on u.id = ee.actor_id
WHERE
u.id > 20000
ORDER BY
u.id DESC,
ee.time DESC;
takes ~25s on my DB, while
SELECT
u.id,
(CAST(data AS JSON) ->> 'Finished') AS Finished,
e.post_value
FROM
users_user u
JOIN events_event e on u.id = e.actor_id
LEFT JOIN events_event ee on ee.actor_id = e.actor_id
AND ee.time > e.time
WHERE
u.id > 20000
AND ee.id IS NULL
ORDER BY
u.id DESC;
takes ~15s.

Joining a subquery with multiple associcated rows in subquery

I have a query that selects a set of Users. Each User can have a number
of Events associated with it. I want to join each User with the earliest Event
associated with that User (resulting in one row per User), and do so within a single query.
So, I kind of want to do this:
SELECT * FROM users
left join (
select * from events where events.user_id = users.id
order by start_time limit 1) as event
ON ("event"."user_id" = "users"."id")
but it is illegal to reference 'users' within the join's select.
You can use a subquery to get the min(start_time) for each user_id. Then you will use this result to join back to the events table to get the details of the min event:
SELECT *
FROM users u
LEFT JOIN
(
SELECT Min(start_time) Min_Start, user_id
FROM events
GROUP BY user_id
) e1
ON u.id = e1.user_id
LEFT JOIN events e2
ON e1.user_id = e2.user_id
AND e1.min_start = e2.start_time
If you are using a database that has the ability to apply a row_number(), then you could use the following:
select *
from
(
SELECT *,
row_number() over(partition by e.user_id order by start_time) rn
FROM users u
LEFT JOIN events e
ON u.id = e.user_id
) src
where rn = 1
In most databases, you can use row_number() for this:
SELECT *
FROM users u left join
(select e.*, row_number() over (partition by e.user_id order by start_time) seqnum
from events e
) e
on e.user_id = u.id
MySQL and MS Access do not support this function, but most other databases do (and you do not specify what database you are using).