How to show just one value result (full outer join) - sql-server

Table and field info as below.
Item ID (Column : Item ID / Desc)
=====================
Item ID Desc
0001 -------- A
0002 -------- B
0003 -------- null
Price Table (Column : Item ID / Price Level(lvl) / Price)
=========================================
Item ID Price Level Price
0001 ----------- 1 -------------3.99
0001 ----------- 2 -------------1.99
0002 ----------- 1 -------------2.99
0003 ----------- 1 -------------5.99
0003 ----------- 2 -------------3.99
(Default Price Level is "1")
so, Every item got the price level 1, some item got the price level 1 and 2
I use the full outer join 'Item' and 'Price' table.
select trs_itm.id, item.desc, price.lvl, price.price from trs_itm
full outer join item on trs_itm.id = item.id
full outer join price on price.id = item.id
group by trs_itm.id, item.desc, price.lvl, price.price
How to show the result of the query as below.
If some item has a price level '2', just show result price level 2.
but the item hasn't price level '2', just show default price level 1.
please, help me.
==============================================
ID Desc lvl price
0001 ------ A ---- 2 -------1.99
0002 ------ B ---- 1 -------2.99
0003 -----null --- 2 -------3.99

I don't know why you are doing a full outer join in this case.
Join the 2 tables and the query that returns the max price level for each item:
select i.*, p.pricelevel, p.price
from item i
inner join price p on p.itemid = i.itemid
inner join (
select itemid, max(pricelevel) pricelevel
from price
group by itemid
) g on g.itemid = p.itemid and g.pricelevel = p.pricelevel
order by i.itemid
Or with not exists:
select i.*, p.pricelevel, p.price
from item i inner join (
select p.* from price p
where not exists (
select 1 from price
where itemid = p.itemid and pricelevel > p.pricelevel
)
) p on p.itemid = i.itemid
order by i.itemid
See the demo.
Results:
> itemid | desc | pricelevel | price
> -----: | :--- | ---------: | ----:
> 1 | A | 2 | 1.99
> 2 | B | 1 | 2.99
> 3 | null | 2 | 3.99

Related

Count number of sale by order amount

I'm using SQL Server 2008 R2 and doing a analysis on a table that contains CustomerID, OrderAmount, RegionID. I need to count number of orders in different categories according to the OrderAmount in each region. And if there is no sales in the category, returns 0.
Sample of data:
CustomerID | OrderAmount | RegionID
10001 | 50 | 801
10002 | 25 | 801
10003 | 200 | 802
10001 | 100 | 802
10002 | 20 | 802
...
And my expected result is:
RegionID | CategoryID | Num_of_Sales
801 | 1 | 2 -----Below 100
801 | 2 | 0 -----100-200
802 | 1 | 2 -----Below 100
802 | 2 | 1 -----100-200
...
My question is:
1. How to return 0 on the category that is empty?
2. Is there a better way to write the code?(Not using UNION)
WITH Category1 AS(
SELECT * FROM Sales_Table
WHERE NewAmount <= 100
)
, Category2 AS(
SELECT * FROM Sales_Table
WHERE NewAmount BETWEEN 101 AND 200
)
, [...]
SELECT Region_ID, CategoryID, Num_of_Sales
FROM (
SELECT Region_ID, COUNT(*) AS [Num_of_Sales], 1 AS CategoryID
FROM Category1
GROUP BY Region_ID
UNION
SELECT Region_ID, COUNT(*) AS [Num_of_Sales], 2 AS CategoryID
FROM Category2
GROUP BY Region_ID
UNION
[...]
)z
ORDER BY Region_ID, CategoryID
So, I use these code and get my result, but the count did not return 0 on the 100-200 Category at Region 801.
A table holding RegionID and CategoryID is needed for what you are trying to achieve. Then we can use that table to do a join as shown below.
With RegCatSales as
(
select RegionID,C,COUNT(*) AS [Num_of_Sales]
from
(
select RegionID,OrderAmount,
CASE
WHEN OrderAmount <= 100 THEN 1
WHEN OrderAmount BETWEEN 101 AND 200 THEN 2
END as C
from Sales_Table x
) xx
group by RegionID, C
),
Regions as
(
select distinct RegionID from RegCatSales
),
Categories as
(
select distinct C from RegCatSales
),
RegCat AS(
select distinct RegionID, C as CategoryID from Regions,Categories
)
select rc.RegionID,rc.CategoryID, ISNULL([Num_of_Sales],0) NUM_Of_Sales from
RegCatSales rcs
right join RegCat rc
on rc.RegionID= rcs.RegionID and rc.CategoryID = rcs.C
order by rc.RegionID, rc.CategoryID

Complicated Sql join query with some condition to get one of the column

I have 2 tables
Table1:
Id | product | price
---+---------+------
1 | A | 5
1 | B | 3
1 | C | 6
Table2:
Id | prod | subprod
---+------+--------
1 | A | xxxx
1 | A | yyyy
1 | A | zzzz
My result table should have all 3 rows from table2 along with a new column called price ( which will be calculated value from table1)
Result table should look like
Id|prod|subprod|price
--+----+-------+-----
1 | A | xxxx |(if subprod = xxxx in table 2 then this should have price of A from table 1)
1 | A | yyyy |(if subprod = yyyy in table 2, then if price of B is greater than price of C then the value should be price of B else 0)
1 | A | zzzz |(if subprod = zzzz in table 2, then if price of B is less than price of C then the value should be price of C-B else 0)
Try this:
select distinct tab_2.*,case when tab_2.subprod ='xxxx' then (select tab_1.price from tab_1 where product='A')
when tab_2.subprod ='yyyy' then CASE WHEN ( select price from tab_1 where product='B') > ( select price from tab_1 where product='C') THEN ( select price from tab_1 where product='B') else 0 end
when tab_2.subprod ='zzzz' then CASE WHEN ( select price from tab_1 where product='B') < ( select price from tab_1 where product='C') THEN ( select price from tab_1 where product='C') - ( select price from tab_1 where product='B') else 0 end
END AS price
from tab_1,tab_2
where tab_1.Id =tab_2.id
Output:-
id prod subprod price
1 A xxxx 5
1 A yyyy 0
1 A zzzz 3
EDIT:
select distinct tab_2.*,case when tab_2.subprod ='xxxx' then (select tab_1.price from tab_1 where product='A')
when tab_2.subprod ='yyyy' then CASE WHEN b.price > c.price THEN b.price else 0 end
when tab_2.subprod ='zzzz' then CASE WHEN b.price < c.price THEN c.price - b.price else 0 end
END AS price
from tab_1,tab_2,( select id,price from tab_1 where product='B') b,( select id,price from tab_1 where product='C')c
where tab_1.Id =tab_2.id
and b.id =tab_2.id
and c.Id =tab_2.id

SQL recursion with full hierarchy

This is what my query looks like right now:
with allmembers (objectid, parentid, name, parentname, recursion) as
(
-- anchor elements: where parentid = 25
select objectid, parentid, name, name as parentname, 0 as recursion
from orgs as orgs1
where parentid = 25
-- recursion begins here
union all
select orgs2.objectid, orgs2.parentid, orgs2.name, orgs3.name as parentname, recursion + 1
from orgs as orgs2
join allmembers as orgs3 on orgs2.parentid = orgs3.objectid
)
-- we select all the results
select *
from allmembers
It selects the orgs (organizations) from a list, where the parentid is 25 (these are the "root organizations") and joins them with all their child organizations, recursively, until there are none left. So we get a list of organizations and their parents.
My problem is that I get only the direct child/parents relationsships:
name | parentname
Sales | All_Employees
Direct Sales | Sales
What is lost in this process is that "Direct Sales" is also a member of "All_Employees", indirectly, through "Sales".
So I would rather have the following result added:
name | parentname
Sales | All_Employees
Direct Sales | Sales
Direct Sales | All_Employees
How to achieve this?
Without going too far and getting functions involved, would using a materialized path suffice for your needs?
create table orgs (objectid int, name varchar(128), parentid int);
insert into orgs values
(26,'All Employees', 25)
,(27,'Sales', 26)
,(28,'Direct Sales',27);
with allmembers as (
-- anchor elements: where parentid = 25
select
objectid
, parentid
, name
, parentname = convert(varchar(128),'')
, rootname = name
, recursion = convert(int,0)
, name_path = convert(varchar(256),name)
from orgs
where parentid = 25
-- recursion begins here
union all
select
c.objectid
, c.parentid
, c.name
, parentname = p.name
, rootname = p.rootname
, recursion = p.recursion + 1
, name_path = convert(varchar(256),p.name_path + ' > ' + c.name)
from orgs as c
join allmembers as p on c.parentid = p.objectid
)
-- we select all the results
select *
from allmembers
returns:
+----------+----------+---------------+---------------+---------------+-----------+--------------------------------------+
| objectid | parentid | name | parentname | rootname | recursion | name_path |
+----------+----------+---------------+---------------+---------------+-----------+--------------------------------------+
| 26 | 25 | All Employees | | All Employees | 0 | All Employees |
| 27 | 26 | Sales | All Employees | All Employees | 1 | All Employees > Sales |
| 28 | 27 | Direct Sales | Sales | All Employees | 2 | All Employees > Sales > Direct Sales |
+----------+----------+---------------+---------------+---------------+-----------+--------------------------------------+

SQL Query combine multiple results or Join tables

I have the Following tables
Disposition Table
Dis_ID | OfferID | RequestID
------------------------------------
34564 | 123 | 9
77456 | 123 | 8
25252 | 124 | 7
46464 | 125 | 10
36464 | 125 | 6
35353 | 125 | 5
Request Table
RequestID | AccountNum |
---------------------------
5 | 548543 |
6 | 548543 |
7 | 684567 |
8 | 684567 |
9 | 684567 |
10 | 548543 |
11 | 684567 |
Rank Table
RankID | OfferId | RequestID | Score
-------------------------------------------
34564 | 123 | 11 | 1
77456 | 124 | 11 | 2
25252 | 125 | 11 | 3
Using the data above I need a query which would behave as follows given a request number look at every record in the Rank Table in this example we have 3 (123, 124, & 125). return the OfferId that appears the fewest times in the Disposition table for this joined account number. in this example offerId 123 appears twice for this account number, offerId 124 appears once and offerId 125 doesn't appears at all for this account number. So offerId 125 should be returned. The offerId which exist in the Rank Table with the fewest appearances in the Disposition table should always be returned unless they are all the same then return the offerId with the lowest value in the Score field. for example if none of the offerIDs appeared in the Dispostion table offerId 123 would return since its Score value is 1.
Resulting table would look something like this
| OfferId | Score | Dis_Occurrences
---------------------------------------------------------------
| 123 | 1 | 2
| 124 | 2 | 1
| 125 | 3 | 0 <--Return this record
This is what I have so far.
SELECT oRank.OfferId, oRank.Rank_Number, count(oRank.OfferId) AS NumDispositions
From Rank oRank
join Request req
on oRank.RequestId = req.RequestId
join Disposition dis
on oRank.OfferId = dis.OfferId
where req.Customer_Account_Number = 684567 and req.RequestId = 11 and oRank.OfferId = dis.OfferId
group by oRank.Rank_Number, oRank.OfferId
order by NumDispositions, oRank.Rank_Number
My incorrect Resulting table looks like this
| OfferId | Score | Dis_Occurrences
---------------------------------------------------------------
| 123 | 1 | 2
| 124 | 2 | 1
| 125 | 3 | 3
It is counting the total number of times the offerId appears in the Disposition Table
EDIT - based on author's comments, here's another version:
Example in SQLFiddle: http://sqlfiddle.com/#!6/d3f99/1/0
with RankReqMap as (
select rnk.OfferId, rnk.Score, reqAcct.AccountNum, reqReq.RequestID
from [Rank] rnk
left join Request reqAcct on reqAcct.RequestID = rnk.RequestID
left join Request reqReq on reqReq.AccountNum = reqAcct.AccountNum
where rnk.RequestID = 11 -- Put your RequestId filter here
)
select oRank.OfferId
,oRank.Score
,count(dis.RequestID) as NumDispositions
from RankReqMap oRank
left join Disposition dis on dis.OfferID = oRank.OfferId
and dis.RequestID = oRank.RequestID
group by oRank.OfferId , oRank.Score
order by NumDispositions, oRank.Score;
ORIGINAL POST
Example in SQLFiddle: http://sqlfiddle.com/#!6/770a8/1/0
This query makes the assumption that you're joining Disposition to Rank based on OfferID, since the RequestIDs for those tables in your example data don't match up. You may have to tweak depending on your needs, but something like the query below should get you the record you're looking for:
-- Gather base data
with RankData as (
select rnk.RankID
,rnk.OfferID
,rnk.RequestID
,rnk.Score
,Dis_Occurrences = count(dis.OfferID)
from dbo.[Rank] rnk
left join dbo.Disposition dis on dis.OfferID = rnk.OfferId
left join dbo.Request req on req.RequestID = rnk.RequestID
group by rnk.RankID, rnk.OfferID, rnk.RequestID, rnk.Score
)
-- Rank count of Dis_Occurrences, taking lowest score into account as a tie breaker
, DispRanking as (
select rdt.*, Dis_Rank = row_number() over (order by Dis_Occurrences asc, rdt.Score asc)
from RankData rdt
)
-- Return only the value with the highest ranking
select * from DispRanking where Dis_Rank = 1
Note also that if you convert the second CTE into a naked SELECT and remove the SELECT statement at the end, you can see all of the records and how they get ranked by the row_number() function:
-- Gather base data
with RankData as (
select rnk.RankID
,rnk.OfferID
,rnk.RequestID
,rnk.Score
,Dis_Occurrences = count(dis.OfferID)
from dbo.[Rank] rnk
left join dbo.Disposition dis on dis.OfferID = rnk.OfferId
left join dbo.Request req on req.RequestID = rnk.RequestID
group by rnk.RankID, rnk.OfferID, rnk.RequestID, rnk.Score
)
-- Output all values, with rankings
select rdt.*, Dis_Rank = row_number() over (order by Dis_Occurrences asc, rdt.Score asc)
from RankData rdt
Good luck!
I think you can use window function for this:
;with disp as(select offerid, count(*) as ocount
from dispositions group by offerid),
rnk as(select r.offerid,
row_number() over(partition by r.requestid
order by isnull(d.ocount, 0), r.score) rn
from ranks r
left join disp d on r.offerid = d.offerid)
select * from rnk where rn = 1

Most recent date and price from multiple tables in SQL Server

I have 5 tables:
contracts, contracts_data, contracts_anexes, anexes, anexes_data
Table contracts columns :
id_contract | date_sign
------------+-----------
1 | 2013-01-03
2 | 2013-06-05
3 | 2014-10-12
Table contracts_data columns :
id_contract | price
------------+------
1 | 100
2 | 200
3 | 300
Table uontracts_anexes columns :
id_contract | id_anex
------------+--------
1 | 1
1 | 2
2 | 3
Table anexes columns :
id_anex | date_of_sign
--------+--------------
1 | 2014-01-03
2 | 2014-06-05
3 | 2015-01-12
Table anexes_Data columns :
id_anex | price
--------+------
1 | 200
2 | 300
3 | 400
Now I need to select price (from contracts_data or anexes_data) where the date of sign is most recent (max date_sign from contracts and anexes), but not all id_contract are in table contracts_anexes (not all contracts have a annex), and one contract (id_contract) may have multiple anexes (multiple rows in contracts_anexes table)
For example
for id_contract = 1 I need to return price 300 and date 2014-06-05,
for id_contract = 2 I need to return price 400 and date 2015-01-12
for id_contract = 3 I need to return price 300 and date 2014-10-12
You could use UNION ALL together with ROW_NUMBER:
;WITH CteUnion AS(
SELECT
id_contract = c.id_contract,
price = cd.price,
date_sign = c.date_sign
FROM contracts c
LEFT JOIN contracts_data cd
ON cd.id_contract = c.id_contract
UNION ALL
SELECT
id_contract = c.id_contract,
price = ad.price,
date_sign = a.date_sign
FROM contracts c
LEFT JOIN contracts_anexes ca
ON ca.id_contract = c.id_contract
LEFT JOIN anexes a
ON a.id_anex = ca.id_anex
LEFT JOIN anexes_data ad
ON ad.id_anex = a.id_anex
)
SELECT
id_contract,
price,
date_sign
FROM(
SELECT *, RN = ROW_NUMBER() OVER(PARTITION BY id_contract ORDER BY date_sign DESC)
FROM CteUnion
)c
WHERE RN = 1
See SQL Fiddle.

Resources