I have a table A
Trade# Trade_DT
1 08/10/2013
2 08/20/2013
and table B
BaseRate EffectiveDT
1.5 08/01/2013
2.0 08/15/2013
3.0 08/25/2013
I want to have a join such that i get the EffectiveDT after the TradeDT
Trade# Trade_DT BaseRate EffectiveDT
1 08/10/2013 2.0 08/15/2013
2 08/20/2013 3.0 08/25/2013
I am guessing that you want the earliest effective date after the Trade_Dt. The following will work in any SQL dialect:
select a.*,
(select min(EffectiveDt)
from b
where b.EffectiveDt > a.TradeDt
) as EffectiveDt
from a;
EDIT:
To get all the values from the b table requires just joining the table back in:
select t.Trade#, t.Trade_DT, b.BaseRate, b.EffectiveDt
from (select a.*,
(select min(EffectiveDt)
from b
where b.EffectiveDt > a.TradeDt
) as EffectiveDt
from a
) t join
b
on a.EffectiveDt = b.EffectiveDt;
in Oracle you can use this:
select distinct Trade,
Trade_DT,
FIRST_VALUE(BaseRate) Over (partition by Trade order by EffectiveDT desc) BaseRate,
FIRST_VALUE(EffectiveDT) Over (partition by Trade order by EffectiveDT desc) EffectiveDT
from tableA
inner join tableB
on tableA.Trade_DT >= tableB.EffectiveDT
And here a demo in SQLFiddle.
This code uses only standard SQL:
SELECT a.*, b.*
FROM TableA a
JOIN TableB b ON b.EffectiveDT =
(SELECT MIN(EffectiveDT)
FROM TableB b1
WHERE a.TradeDT < b1.EffectiveDT)
Related
I am joining 3 tables. In tables 2 and 3, records do not exist for some of the IDs in table 1. This of course, yields null values for these instances in my joined table.
I know I can replace these nulls with coalesce, but I don't know how to replace the nulls conditionally. In my reprex below, I would like to replace a null with an ID's max total (if an ID has a non-null total under a different job title) or replace a null with 0 (if an ID has null totals under both of their job titles).
Reprex code below:
/*sample table a (contains full data - 2 records for each ID)*/
data table_a;
input id title $ region $ calls;
cards;
1 manager south 30
1 agent north 20
2 manager west 20
2 agent south 25
;
run;
/*sample table b (missing an agent record for ID 1 -- will result in null total sales for ID 1's agent record in joined table)*/
data table_b;
input id title $ sales;
cards;
1 manager 20
2 manager 5
2 agent 3
;
run;
/*sample table c (missing both records for ID 2 - will result in null total_leads for ID 2 in joined table)*/
data table_c;
input id title $ leads;
cards;
1 manager .
1 agent 10
;
run;
/*join tables*/
proc sql;
create table reprex as
select a.id,
a.region,
a.calls,
a.title,
coalesce(b.total_sales, 0) as total_sales, /*this replaces all nulls as 0, but I'd like to replace
b.sales, them conditionally */
coalesce(c.total_leads, 0) as total_leads,
c.leads
from table_a as a
left join (select sum(coalesce(sales, 0)) as total_sales, sales, id, title from table_b group by id) b
on a.id = b.id and a.title = b.title
left join (select sum(coalesce(leads, 0)) as total_leads, leads, id, title from table_c group by id) c
on a.id = c.id and a.title = c.title;
quit;
Ended up nesting subqueries to get what I was after:
proc sql;
create table reprex as
select id, region, title,
calls,
case
when max_total_calls is null then 0
else max_total_calls
end as total_calls,
sales,
case
when max_total_sales is null then 0
else max_total_sales
end as total_sales,
leads,
case
when max_total_leads is null then 0
else max_total_leads
end as total_leads
from
(select *,
max(total_sales) as max_total_sales,
max(total_leads) as max_total_leads,
max(total_calls) as max_total_calls
from
(select a.id, a.region, a.calls, a2.total_calls, a.title,
b.sales, c.total_leads, c.leads
from table_a as a
left join (select sum(coalesce(calls, 0)) as total_calls, calls, id, title from table_a group by id) a2
on a.id = a2.id and a.title = a2.title
left join (select sum(coalesce(sales, 0)) as total_sales, max(coalesce(sales,0)) as max_sales, sales, id, title from table_b group by id) b
on a.id = b.id and a.title = b.title
left join (select sum(coalesce(leads, 0)) as total_leads, leads, id, title from table_c group by id) c
on a.id = c.id and a.title = c.title)
group by id);
quit;
This question already has answers here:
How do I limit the number of rows returned by an Oracle query after ordering?
(14 answers)
Closed 3 years ago.
SELECT
BRANCH_ADD,
COUNT(TRANS_AMOUNT) AS TRANS
FROM
(SELECT
A.BRANCH_ADD, C.TRANS_AMOUNT
FROM
BRANCHES A, ACCTS B, TRANSACTION C
WHERE
a.branch_code = b.branch_code
AND b.acct_no = c.acct_no
ORDER BY
BRANCH_ADD)
GROUP BY
BRANCH_ADD;
Output of the above query :
as text :
BRANCH_ADD TRANS
------------ ------
TOWN_HALL 2
CHAMRAJPET 4
CITY_MARKET 4
You need some subquery and filter for the count = to max
SELECT BRANCH_ADD, my_count AS max_value
FROM (
SELECT
A.BRANCH_ADD
, count(C.TRANS_AMOUNT ) my_count
FROM BRANCHES A
INNER JOIN ACCTS B ON a.branch_code=b.branch_code
INNER JOIN TRANSACTION C ON b.acct_no=c.acct_no
GROUP BY A.BRANCH_ADD;
)
WHERE my_count = (
select max(my_count)
from (
SELECT
A.BRANCH_ADD
, count(C.TRANS_AMOUNT ) my_count
FROM BRANCHES A
INNER JOIN ACCTS B ON a.branch_code=b.branch_code
INNER JOIN TRANSACTION C ON b.acct_no=c.acct_no
GROUP BY A.BRANCH_ADD;
)
)
Use ROW_NUMBER() to return the 1st row only:
SELECT t.BRANCH_ADD, t.TRANS FROM (
SELECT A.BRANCH_ADD, COUNT(C.TRANS_AMOUNT) TRANS,
ROW_NUMBER() OVER (ORDER BY COUNT(C.TRANS_AMOUNT)) rn
FROM BRANCHES A
INNER JOIN ACCTS B ON A.BRANCH_CODE = B.BRANCH_CODE
INNER JOIN TRANSACTION C ON B.ACCT_NO = C.ACCT_NO
GROUP BY BRANCH_ADD
) t
WHERE t.rn = 1
If you are using Oracle version 12+ you can also do it with FETCH:
SELECT A.BRANCH_ADD, COUNT(C.TRANS_AMOUNT) TRANS
FROM BRANCHES A
INNER JOIN ACCTS B ON A.BRANCH_CODE = B.BRANCH_CODE
INNER JOIN TRANSACTION C ON B.ACCT_NO = C.ACCT_NO
GROUP BY BRANCH_ADD
ORDER BY TRANS
FETCH FIRST 1 ROWS ONLY
You can use analytical functions
such as MIN(..) KEEP (DENSE_RANK FIRST ORDER BY ..) OVER (PARTITION BY ..):
SELECT * FROM
(
SELECT A.BRANCH_ADD, C.TRANS_AMOUNT,
MIN(C.TRANS_AMOUNT)
KEEP (DENSE_RANK FIRST ORDER BY C.TRANS_AMOUNT)
OVER (PARTITION BY 0) AS lowest
FROM BRANCHES A
JOIN ACCTS B ON B.branch_code = A.branch_code
JOIN TRANSACTION C ON C.acct_no = B.acct_no
)
WHERE TRANS_AMOUNT = lowest
My query is giving me result from grouped data but now I want only two rows
I have tried HAVING COUNT(*) <= 2 but issue is is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
my query is
select f.CompanyName, f.EmployeeCity, f.PrioritySL ,f.EmployeeSeniorityLevel ,f.EmployeeID
from (
select ConcatKey, min(PrioritySL) as PSL
from dbo.WalkerItContacts group by ConcatKey
) as x inner join dbo.WalkerItContacts as f on f.ConcatKey = x.ConcatKey and f.PrioritySL = x.PSL
where f.PrioritySL != '10'
Company apple have 9 records I want only 2 records
my data
company name priority
a 10
a 1
a 3
b 2
b 4
b 3
b 5
c 1
c 10
c 2
my expected data
company name priority
a 1
a 3
b 2
b 3
c 1
c 2
Add a 'top 2' clause to the outer query:
select top 2 f.CompanyName, f.EmployeeCity, f.PrioritySL ,f.EmployeeSeniorityLevel ,f.EmployeeID
from (
select ConcatKey, min(PrioritySL) as PSL
from dbo.WalkerItContacts group by ConcatKey
) as x inner join dbo.WalkerItContacts as f on f.ConcatKey = x.ConcatKey and f.PrioritySL = x.PSL
where f.PrioritySL != '10'
and f.CompanyName= 'Apple'
will give you two rows. Add a order clause by in the outer query so you can control which two rows are returned.
You can phrase this more succinctly and with better performance as:
select top (2) wic.*
from (select wic,
rank() over (partition by CompanyName, ConcatKey order by PrioritySL) as seqnum
from dbo.WalkerItContacts wic
) wic
where seqnum = 1 and
wic.PrioritySL <> 10 and
wic.CompanyName = 'Apple';
I think you could solve your problem using the ROW_NUMBER() function to count the rows and filter it in the WHERE clause to only show 2 rows per group.
I think something like this might work for you:
SELECT rownum, f.CompanyName, f.EmployeeCity, f.PrioritySL,
f.EmployeeSeniorityLevel, f.EmployeeID
FROM ( SELECT ConcatKey, MIN(PrioritySL) AS PSL, ROW_NUMBER() OVER(PARTITION BY
f.CompanyName) AS rownum
FROM dbo.WalkerItContacts
GROUP BY ConcatKey) AS x
INNER JOIN dbo.WalkerItContacts AS f ON f.ConcatKey = x.ConcatKey
AND f.PrioritySL = x.PSL
WHERE f.PrioritySL != '10' AND rownum <= 2
ORDER BY f.CompanyName ASC;
Hope this helps some.
I have two different sources of information and I am trying to marry them together to get information relating to a customer and their last bill. I have managed to get details of each customer and their max billing period end date but I am unsure as to how I can then get the details from the associated bill. I have the following query:
SELECT new_mprnnumber,
new_customernumber,
MAX(b.billingPeriodEndDate) as 'Billed up to date'
FROM [CRM].[crm4_MSCRM].[dbo].[AccountExtensionBase] as a
inner join Billing.dbo.bill as b
on a.new_mprnnumber = b.MPRN
where new_accountstage = 7
and new_accounttype = 2
group by new_mprnnumber,
new_customernumber
GO
The bill has fields like amount due etc but I only want to return details of those from the max dated bill, any help would be greatly appreciated
Use a CTE with row_number()
with CTE as
(
select a.new_mprnnumber,
a.new_customernumber,
b.*,
row_number()
over (partition by new_customernumber -- add additional partitions as you would group bys
order by billingPeriodEndDate desc) as r_ord
from AccountExtensionBase a
inner join bill b
on a.new_mprnnumber = b.MPRN
where new_accountstage = 7
and new_accounttype = 2
)
select *
from CTE
where r_ord = 1
Put your query into a CTE and then link back to table bill:
WITH CTE AS (
SELECT new_mprnnumber,
new_customernumber,
MAX(b.billingPeriodEndDate) as MaxBillDate
FROM [CRM].[crm4_MSCRM].[dbo].[AccountExtensionBase] as a
inner join Billing.dbo.bill as b
on a.new_mprnnumber = b.MPRN
where new_accountstage = 7
and new_accounttype = 2
group by new_mprnnumber,
new_customernumber
)
SELECT b.*
FROM CTE c
INNER JOIN Billing.dbo.bill b ON c.MaxBillDate = b.billingPeriodEndDate AND c.new_mprnnumber = b.MPRN
I'm trying to print out the results from the "GermanDB" Database first, while also showing everything from the Boston DB that was not in the German database. Can this be done in one query?
My query (the bold part functions but does not order the way I want)
select * from (
SELECT DISTINCT a.ProductRef
FROM GERMANDB.dbo.LOCATIONS AS a INNER JOIN GERMANDB.dbo.ITEMS AS b ON a.ProductRef = b.ProductRef
WHERE b.ACTIVE=1
) ta
UNION select * from
SELECT DISTINCT c.ProductRef
FROM BOSTONDB.dbo.LOCATIONS AS c INNER JOIN BOSTONDB.dbo.ITEMS AS d ON c.ProductRef = d.ProductRef
WHERE c.ACTIVE=1 (c.ProductRef NOT IN
(SELECT ProductRef FROM GERMANDB.dbo.ITEMS where ACTIVE=1))
) tb
order by ta.ProductRef** , tb.productRef
Just add one field to signal the priority. Like this:
select *, 0 as Priority from (
SELECT DISTINCT a.ProductRef
FROM GERMANDB.dbo.LOCATIONS AS a INNER JOIN GERMANDB.dbo.ITEMS AS b ON a.ProductRef = b.ProductRef
WHERE b.ACTIVE=1
) ta
UNION select *, 1 as Priority from
SELECT DISTINCT c.ProductRef
FROM BOSTONDB.dbo.LOCATIONS AS c INNER JOIN BOSTONDB.dbo.ITEMS AS d ON c.ProductRef = d.ProductRef
WHERE c.ACTIVE=1 (c.ProductRef NOT IN
(SELECT ProductRef FROM GERMANDB.dbo.ITEMS where ACTIVE=1))
) tb
order by Priority, ProductRef