multiply columns in pivot query - sql

I have the following query below which works fine.
;with c as
(
select company, regionId, isnull(profit, 0) profit from MyTable
where pricedate = '2015-11-30'
)
select * from c as source pivot (max(profit ) for regionId in ([EU], [US], [JP])) as pvt
I would however like to make an adjustment to the query.
Where the regionId is equal to US I would like to multiple the profit by 0.
5 when the regionId is equal to EU I would like to multiple the profit by 0.4 & when its JP multiple the profit by 0.1.
I'm not sure about the best way of doing this? Should I do this before the pivot using a case statement or is there a better way to do it?

Try like this,
;WITH c
AS (SELECT company,
regionId,
Isnull(profit, 0) profit
FROM MyTable
WHERE pricedate = '2015-11-30')
SELECT c.company,
c.regionid,
CASE
WHEN c.regionid = 'US' THEN c.profit * 0.5
WHEN c.regionid = 'EU' THEN c.profit * 0.4
WHEN c.regionid = 'JP' THEN c.profit * 0.1
ELSE c.profit
END AS profit
FROM c
PIVOT (Max(profit )
FOR regionId IN ([EU],
[US],
[JP])) AS pvt

create table #mytable (company varchar(20), regionid varchar(10), profit money, pricedate date)
insert into
#mytable
select 'csc', 'EU', 1000,'2015-11-30'
union
select 'csc', 'US', 2000,'2015-11-30'
union
select 'csc', 'JP', 3000,'2015-11-30'
union
select 'hp', 'EU', 5000,'2015-11-30'
union
select 'hp', 'US', 4000,'2015-11-30'
union
select 'hp', 'JP', 3000,'2015-11-30'
union
select 'csc', 'EU', 1100,'2015-11-30'
union
select 'hp', 'EU', 5600,'2015-11-30'
;with c as
(
select company, regionId,
case WHEN regionid = 'US' THEN isnull(profit, 0) * 0.5
WHEN regionid = 'EU' THEN isnull(profit, 0) * 0.4
WHEN regionid = 'JP' THEN isnull(profit, 0) * 0.1
ELSE isnull(profit, 0)
END
AS profit
from #MyTable
where pricedate = '2015-11-30'
)
select * from c as source pivot (max(profit ) for regionId in ([EU], [US], [JP])) as pvt

Related

Insert Into Third Table Based Two Another- oracle

I have these three tables; how can I insert into Table C (c_table) FROM b_table and a_table assuming that the volume of table's (especially A) data is very large:
Table B:
CREATE TABLE b_table
(
NAME varchar2(10),
ID number PRIMARY KEY
)
Table A:
CREATE TABLE a_table
(
CODE number,
RATE number,
DATEE date,
CONSTRAINT fk_a_table
FOREIGN KEY (CODE) REFERENCES b_table(ID)
);
Table C:
CREATE TABLE c_table
(
DATEE date,
USD number,
EUR number,
OBP number
);
You can use conditional aggregation:
insert into c_table (DATEE, USD, EUR, OBP)
select a.DATEE,
max(case WHEN a.code = 1 THEN a.rate end),
max(case WHEN a.code = 2 THEN a.rate end),
max(case WHEN a.code = 3 THEN a.rate end)
from a_table a
group by a.DATEE
begin
for r in(
with m as(
select 1 as xcode, 1000 as xrate, sysdate as xdate from dual
union all
select 2, 2000, sysdate from dual
union all
select 3, 3000, sysdate-1 from dual
union all
select 1, 2000, sysdate from dual
union all
select 2, 4000, sysdate from dual
union all
select 3, 5000, sysdate-2 from dual
)
select * from m
pivot(
sum(xrate) for xcode in (
1 as usd,
2 as eur,
3 as gbp
))
)loop
update your_table set foo = r.xrate, foo_currency_usd = x.usd where foo_date = r.date;
end loop;
end;
Try the below code.
INSERT INTO c_table(DATEE, USD, EUR, OBP)
Select DATEE, USD, EUR, OBP from(
Select
TO_CHAR(a.DATEE,'DD-MON-YYYY') DATEE, a.Rate, b.name from a_table a
inner join b_table b on b.id=a.code
)PIVOT(SUM(rate)for name in ('USD' USD, 'EUR' EUR,'OBP' OBP));

SQL - split values into buckets using reference table

I've tried to describe the challenge below; but perhaps the best way to understand might be to run the sample SQL and work backwards from the sample output table (#IncomingSplitBucket)
Im certain there is an eloquent way to code this – but its beyond any of my best efforts.
The challenge is titled:
Splitting Numbers into Buckets
Each Customer has Payment Buckets (#bucket). I’m looking to Assign payments into Buckets as the payments are made see table (#incoming).
Payments can span buckets and can be for +/- amounts.
Using the (#incoming) and (#buckets) information – the AIM is to assign the payments into buckets. Payments should be split when they span a bucket amount.
The table #IncomingSplitBucket – provide the desired OUTPUT. One way to understand the requirements is to perhaps look at this and work backwards.
I have tried and failed many approaches to this problem.
/*
Please run code and review
*/
--===========================================
--t1 - PAYMENT SCHEDULE SPLIT INTO BUCKET
--===========================================
DECLARE #bucket TABLE (
CustID INT,
BucketSeqID char(1),
Amount money
)
INSERT INTO #bucket
SELECT 1,'a', '1000' union
SELECT 1,'b', '1000' union
SELECT 1,'c', '2000' union
SELECT 1,'d', '2000' union
SELECT 2,'a', '5000'union
SELECT 2,'b', '5000'union
SELECT 2,'c', '1000'union
SELECT 2,'d', '1000'union
SELECT 3,'a', '5000' union
SELECT 3,'b', '5000'
--===========================================
--t2 - PAYMENTS COMING IN
--===========================================
DECLARE #incoming TABLE (
CustID INT,
IncomingSeqID INT,
Amount money
)
INSERT INTO #incoming
SELECT 1,1, '1000' union
SELECT 1,2, '2000' union
SELECT 1,3, '3000' union
SELECT 2,1, '5000' union
SELECT 2,2, '3000' union
SELECT 2,3, '2000' union
SELECT 2,4, '2000' union
SELECT 3,1, '3000' union
SELECT 3,2, '3000' union
SELECT 3,3, '3000' union
SELECT 3,4, '1000'
--=================================================================
--t3 - THIS IS WHAT THE OUTPUT DATA SHOULD LOOK LIKE
--================================================================
DECLARE #IncomingSplitBucket TABLE (
CustID INT,
IncomingSeqID INT,
BucketSeqID char(1),
AmountBucket money
)
INSERT INTO #IncomingSplitBucket
SELECT 1,1,'a','1000' union
SELECT 1,2,'b','1000' union
SELECT 1,2,'c','1000' union
SELECT 1,3,'c','1000' union
SELECT 1,3,'d','2000' union
SELECT 2,1,'a','5000' union
SELECT 2,2,'b','3000' union
SELECT 2,3,'b','2000' union
SELECT 2,4,'c','1000' union
SELECT 2,4,'d','1000' union
SELECT 3,1,'a','3000' union
SELECT 3,2,'a','2000' union
SELECT 3,2,'b','1000' union
SELECT 3,3,'b','3000' union
SELECT 3,4,'b','1000'
--=================================================================
--Outputs and Data Checks
--================================================================
--REVIEW DATA
select * from #bucket
select * from #incoming
select * from #IncomingSplitBucket --(sample output)
--DATA Check - The SUM AmountBucket of Grouped BucketSeqID = the #bucket amounts see table
SELECT CustID, BucketSeqID, SUM(AmountBucket) AS BucketCheck
FROM #IncomingSplitBucket
GROUP BY CustID, BucketSeqID
order by 1,2
--DATA Check - The SUM AmountBucket of Grouped IncomingSeqID = the #incoming amounts see table
SELECT CustID, IncomingSeqID, SUM(AmountBucket) AS BucketCheck
FROM #IncomingSplitBucket
GROUP BY CustID, IncomingSeqID
order by 1,2
Updated complexity request: (10/12/2019)
When negative amounts are received that take money out of the
buckets.
When the amount received is greater than buckets – an ‘overflow
bucket’ is used (called ‘x’ in the Expect Output)
Thanks
--===========================================
--t1 - BUCKETS
--===========================================
DECLARE #bucket TABLE (
CustID INT,
BucketSeqID char(1),
Amount money
)
INSERT INTO #bucket
SELECT 1,'a', '1000' union
SELECT 1,'b', '1000' union
SELECT 1,'c', '2000' union
SELECT 1,'d', '2000' union
SELECT 2,'a', '5000'union
SELECT 2,'b', '5000'union
SELECT 2,'c', '1000'union
SELECT 2,'d', '1000'union
SELECT 3,'a', '5000' union
SELECT 3,'b', '5000'
--===========================================
--t2 - PAYMENTS
--===========================================
DECLARE #incoming TABLE (
CustID INT,
IncomingSeqID INT,
Amount money
)
INSERT INTO #incoming
SELECT 1,1, '1000' union
SELECT 1,2, '2000' union
SELECT 1,3, '3000' union
SELECT 2,1, '5000' union
SELECT 2,2, '3000' union
SELECT 2,3, '2000' union
SELECT 2,4, '2000' union
SELECT 2,5, '-3000' union
SELECT 3,1, '3000' union
SELECT 3,2, '3000' union
SELECT 3,3, '3000' union
SELECT 3,4, '500' union
SELECT 3,5, '200' union
SELECT 3,6, '-500' union
SELECT 3,7, '800' union
SELECT 3,8, '-400' union
SELECT 3,9, '500'
--=================================================================
--t3 - EXPECTED OUTPUT
--================================================================
DECLARE #IncomingSplitBucket TABLE (
CustID INT,
IncomingSeqID INT,
BucketSeqID char(1),
AmountBucket money
)
INSERT INTO #IncomingSplitBucket
SELECT 1,1,'a','1000' union
SELECT 1,2,'b','1000' union
SELECT 1,2,'c','1000' union
SELECT 1,3,'c','1000' union
SELECT 1,3,'d','2000' union
SELECT 2,1,'a','5000' union
SELECT 2,2,'b','3000' union
SELECT 2,3,'b','2000' union
SELECT 2,4,'c','1000' union
SELECT 2,4,'d','1000' union
SELECT 2,5,'d','-1000' union
SELECT 2,5,'c','-1000' union
SELECT 2,5,'b','-1000' union
SELECT 3,1,'a','3000' union
SELECT 3,2,'a','2000' union
SELECT 3,2,'b','1000' union
SELECT 3,3,'b','3000' union
SELECT 3,4,'b','200' union
SELECT 3,5,'b','-500' union
SELECT 3,6,'b','800' union
SELECT 3,7,'b','-400' union
SELECT 3,8,'b','400' union
SELECT 3,8,'x','100'
--=================================================================
--Outputs and Data Checks
--================================================================
--REVIEW DATA
select * from #bucket
select * from #incoming
select * from #IncomingSplitBucket --(expected output)
--DATA Check - The SUM AmountBucket of Grouped BucketSeqID = the #bucket amounts see table
SELECT CustID, BucketSeqID, SUM(AmountBucket) AS BucketCheck
FROM #IncomingSplitBucket
GROUP BY CustID, BucketSeqID
order by 1,2
--DATA Check - The SUM AmountBucket of Grouped IncomingSeqID = the #incoming amounts see table
SELECT CustID, IncomingSeqID, SUM(AmountBucket) AS BucketCheck
FROM #IncomingSplitBucket
GROUP BY CustID, IncomingSeqID
order by 1,2
First I will use a common table expression (cte) to change the column names so I dont have similar ones between tables to make our life easy, plus will convert the bucket names a,b,c,d to a seq of 1,2,3,4 for simplicity.
Then I will continue with another recursive cte to take the first bucket and the first incoming payment, if the bucket is not filled for the next record I will use the same unfilled bucket else i will use the next bucket, same thing with the incoming payment if the incoming payment fits in the remaining part of the bucket on next row i will go to the next payment if not I will use the rest of the incoming payment till all incoming payment are finished.
Please see the below CTE
;with bucket as (
select CustID BucketCustID,BucketSeqID,
case BucketSeqID when 'a' then 1 when 'b' then 2 when 'c' then 3 when 'd' then 4 end BucketSeq
,Amount bucketAmount from #bucket
),incoming as (
select CustID IncomingCustID, IncomingSeqID ,Amount [IncomingAmount] from #incoming
),result as (
select BucketCustID,IncomingSeqID,BucketSeqID,BucketSeq
,case when bucketAmount<IncomingAmount then 0 else bucketAmount-IncomingAmount end bucketAmount
,case when bucketAmount>IncomingAmount then 0 else IncomingAmount-bucketAmount end IncomingAmount
,case when bucketAmount>IncomingAmount then IncomingAmount else bucketAmount end InBucket
from bucket b
inner join incoming i on i.IncomingCustID=b.BucketCustID and i.IncomingSeqID=1
where b.BucketSeq=1
union all
select BucketCustID,IncomingSeqID,BucketSeqID,BucketSeq
,case when bucketAmount<IncomingAmount then 0 else bucketAmount-IncomingAmount end bucketAmount
,case when bucketAmount>IncomingAmount then 0 else IncomingAmount-bucketAmount end IncomingAmount
,case when bucketAmount>IncomingAmount then IncomingAmount else bucketAmount end InBucket
from (
select
b.BucketCustID,i.IncomingSeqID,b.BucketSeqID,b.BucketSeq
,case when r.BucketSeq=b.BucketSeq then r.bucketAmount else b.bucketAmount end bucketAmount
,case when r.IncomingSeqID=i.IncomingSeqID then r.IncomingAmount else i.IncomingAmount end IncomingAmount
from result r
inner join bucket b on b.BucketCustID=r.BucketCustID and b.BucketSeq=r.BucketSeq+(case when r.bucketAmount=0 then 1 else 0 end)
inner join incoming i on i.IncomingCustID=r.BucketCustID and i.IncomingSeqID=r.IncomingSeqID+(case when r.IncomingAmount=0 then 1 else 0 end)
) Prev
)
select BucketCustID CustID,IncomingSeqID,BucketSeqID,InBucket AmountBucket
from result r
order by BucketCustID,IncomingSeqID,BucketSeqID
The output matches your desired output as below:-
CustID IncomingSeqID BucketSeqID AmountBucket
1 1 a 1000.00
1 2 b 1000.00
1 2 c 1000.00
1 3 c 1000.00
1 3 d 2000.00
2 1 a 5000.00
2 2 b 3000.00
2 3 b 2000.00
2 4 c 1000.00
2 4 d 1000.00
3 1 a 3000.00
3 2 a 2000.00
3 2 b 1000.00
3 3 b 3000.00
3 4 b 1000.00

How to cast to varchar and add % sign to SQL

Ive been pulling my hair out to get around this..how can i convert this to a varchar and add the % symbol to the results?
I have placed the code below of the query and have tried to cast it but keep getting errors that i don't know how to fix. Can someone pleae make some suggestions on how to either simplify and prevent divide by zero issues also.
select 'Conversion Rate' as Type,
(
SELECT
(
COALESCE
(
CAST(CAST(nullif(t.ConvertedTrials,0) AS NUMERIC(18,2)) / (CAST(nullif(t.UnconvertedTrials,0) AS NUMERIC(18,2))) * 100 as decimal(10,2))
,
0
)
) AS Percentage
FROM (
SELECT
UnconvertedTrials = (
SELECT count(*) FROM
(
select memberid from membership where discountcodeid = '79a7fd7c-9ebe-4ec0-8ac0-95ea274f1f64' Group by membership.memberid
) as a
),
ConvertedTrials = (
SELECT count(*) FROM
(
SELECT membership.memberid
FROM Membership, Package
WHERE membership.PackageId = Package.Id
AND Package.PackageTypeId != 1
AND membership.memberid in (select memberid from membership where discountcodeid = '79a7fd7c-9ebe-4ec0-8ac0-95ea274f1f64')
Group by membership.memberid
) as b
)
) as t
) as Total
select 'Conversion Rate' as Type,
RTRIM(CAST((
SELECT
(
COALESCE
(
CAST(CAST(nullif(t.ConvertedTrials,0) AS NUMERIC(18,2)) / (CAST(nullif(t.UnconvertedTrials,0) AS NUMERIC(18,2))) * 100 as decimal(10,2))
,
0
)
) AS Percentage
FROM (
SELECT
UnconvertedTrials = (
SELECT count(*) FROM
(
select memberid from membership where discountcodeid = '79a7fd7c-9ebe-4ec0-8ac0-95ea274f1f64' Group by membership.memberid
) as a
),
ConvertedTrials = (
SELECT count(*) FROM
(
SELECT membership.memberid
FROM Membership, Package
WHERE membership.PackageId = Package.Id
AND Package.PackageTypeId != 1
AND membership.memberid in (select memberid from membership where discountcodeid = '79a7fd7c-9ebe-4ec0-8ac0-95ea274f1f64')
Group by membership.memberid
) as b
)
) as t
) AS NVARCHAR(50))) + '%' as Total
your actual percentage is quite a large sub-query, it's hard to see how to enclose it in brackets - I had about 10 goes - is it any good yet?
maybe it's easier to wrap the whole query in another select e.g
SELECT A,B FROM (<your query>) AS AQUERY
(which should give exactly same results as your query) - THEN work on formatting B in the outer query
Here is my second way of doing it
SELECT ORIGINAL.Type, RTRIM(CAST(ORIGINAL.Total as varchar(80))) + '%' AS Total
FROM
(
select 'Conversion Rate' as Type,
(
SELECT
(
COALESCE
(
CAST(CAST(nullif(t.ConvertedTrials,0) AS NUMERIC(18,2)) / (CAST(nullif(t.UnconvertedTrials,0) AS NUMERIC(18,2))) * 100 as decimal(10,2))
,
0
)
) AS Percentage
FROM (
SELECT
UnconvertedTrials = (
SELECT count(*) FROM
(
select memberid from membership where discountcodeid = '79a7fd7c-9ebe-4ec0-8ac0-95ea274f1f64' Group by membership.memberid
) as a
),
ConvertedTrials = (
SELECT count(*) FROM
(
SELECT membership.memberid
FROM Membership, Package
WHERE membership.PackageId = Package.Id
AND Package.PackageTypeId != 1
AND membership.memberid in (select memberid from membership where discountcodeid = '79a7fd7c-9ebe-4ec0-8ac0-95ea274f1f64')
Group by membership.memberid
) as b
)
) as t
) as Total
) ORIGINAL

Intersect Select Statements on Specific Columns

I've a table of SalesDetails, looking like this:
InvoiceID, LineID, Product
1,1,Apple
1,2,Banana
2,1,Apple
2,2,Mango
3,1,Apple
3,2,Banana
3,3,Mango
My requirement is to return rows where an Invoice contained sales of both: Apple AND Banana, but if there are other products on such an invoice, I don't want those.
So the result should be:
1,1,Apple
1,2,Banana
3,1,Apple
3,2,Banana
I tried the following:
Select * from SalesDetails where Product = 'Apple'
Intersect
Select * from SalesDetails where Product = 'Banana'
Didn't work, because it seems Intersect needs to match all the columns.
What I'm hoping to do is:
Select * from SalesDetails where Product = 'Apple'
Intersect ----On InvoiceID-----
Select * from SalesDetails where Product = 'Banana'
Is there a way to do this?
Or do I have to first Intersect on InvoiceIDs only using my criteria, then select the rows of those InvoiceIDs where the criteria is matched again, I.e.:
Select * From SalesDetails
Where Product In ('Apple', 'Banana') And InvoiceID In
(
Select InvoiceID from SalesDetails where Product = 'Apple'
Intersect
Select InvoiceID from SalesDetails where Product = 'Banana'
)
Which seems somewhat wasteful as it's examining the criteria twice.
Okay this time I've managed to get reuse of the Apple/Banana info by using a CTE.
with sd as (
Select * from SalesDetails
where (Product in ('Apple', 'Banana'))
)
Select * from sd where invoiceid in (Select invoiceid from
sd group by invoiceid having Count(distinct product) = 2)
SQL Fiddle
Do it with conditional aggregation:
select *
from SalesDetails
where product in ('apple', 'banana') and invoiceid in(
select invoiceid
from SalesDetails
group by invoiceid
having sum(case when product in('apple', 'banana') then 1 else 0 end) >= 2)
I think OP's suggestion is about the best one can do. The following might be faster, although I expect the difference to be slight and I have not done any benchmarking.
Select * From SalesDetails
Where Product ='Apple' And InvoiceID In
(
Select InvoiceID from SalesDetails where Product = 'Banana'
)
union all
select * from SalesDetails
Where Product ='Banana' And InvoiceID In
(
Select InvoiceID from SalesDetails where Product = 'Apple'
)
A self-join will solve the problem.
SELECT T1.*
FROM SalesDetails T1
INNER JOIN SalesDetails T2 ON T1.InvoiceId = T2.InvoiceId
AND (T1.Product = 'Apple' AND T2.Product = 'Banana'
OR T1.Product = 'Banana' AND t2.Product = 'Apple')
declare #t table (Id int,val int,name varchar(10))
insert into #t (id,val,name)values
(1,1,'Apple'),
(1,2,'Banana'),
(2,1,'Apple'),
(2,2,'Mango'),
(3,1,'Apple'),
(3,2,'Banana'),
(3,3,'Mango')
;with cte as (
select ID,val,name,ROW_NUMBER()OVER (PARTITION BY id ORDER BY val)RN from #t)
,cte2 AS(
select TOP 1 c.Id,c.val,c.name,C.RN from cte c
WHERE RN = 1
UNION ALL
select c.Id,c.val,c.name,C.RN from cte c
WHERE c.Id <> c.val)
select Id,val,name from (
select Id,val,name,COUNT(RN)OVER (PARTITION BY Id )R from cte2 )R
WHERE R = 2
Other was is to do PIVOT like this:
DECLARE #DataSource TABLE
(
[InvoiceID] TINYINT
,[LineID] TINYINT
,[Product] VARCHAR(12)
);
INSERT INTO #DataSource ([InvoiceID], [LineID], [Product])
VALUES (1,1,'Apple')
,(1,2,'Banana')
,(2,1,'Apple')
,(2,2,'Mango')
,(3,1,'Apple')
,(3,2,'Banana')
,(3,3,'Mango');
SELECT *
FROM #DataSource
PIVOT
(
MAX([LineID]) FOR [Product] IN ([Apple], [Banana])
) PVT
WHERE [Apple] IS NOT NULL
AND [Banana] IS NOT NULL;
It will give you the results in this format, but you are able to UNVPIVOT them if you want:
Or you can use window function like this:
;WITH DataSource AS
(
SELECT *
,SUM(1) OVER (PARTITION BY [InvoiceID]) AS [Match]
FROM #DataSource
WHERE [Product] = 'Apple' OR [Product] = 'Banana'
)
SELECT *
FROM DataSource
WHERE [Match] =2
First, you want to COUNT the number of rows per InvoiceID that matched the criteria Product = 'Apple' or 'Banana'. Then do a SELF-JOIN and filter the rows such that the COUNT must be >= 2, or the number of Products in your critera.
SQL Fiddle
SELECT sd.*
FROM (
SELECT InvoiceID, CC = COUNT(*)
FROM SalesDetails
WHERE Product IN('Apple', 'Banana')
GROUP BY InvoiceID
)t
INNER JOIN SalesDetails sd
ON sd.InvoiceID = t.InvoiceID
WHERE
t.CC >= 2
AND sd.Product IN('Apple', 'Banana')
WITH cte
AS
(
SELECT *
FROM [dbo].[SalesDetails]
WHERE [Product]='banana')
,cte1
AS
(SELECT *
FROM [dbo].[SalesDetails]
WHERE [Product]='apple')
SELECT *
FROM cte c INNER JOIN cte1 c1
ON c.[InvoiceID]=c1.[InvoiceID]
Here is a method using window functions:
select sd.*
from (select sd.*,
max(case when product = 'Apple' then 1 else 0 end) over (partition by invoiceid) as HasApple,
max(case when product = 'Banana' then 1 else 0 end) over (partition by invoiceid) as HasBanana
from salesdetails sd
) sd
where (product = 'Apple' and HasBanana > 0) or
(product = 'Banana' and HasApple > 0);
If you only want to write the condition once and are sure that each Product will only be once in any Order, you can use this:
SELECT * FROM (
SELECT InvoiceID, Product
,COUNT(*) OVER (PARTITION BY InvoiceID) matchcount
FROM SalesDetails
WHERE Product IN ('Apple','Banana') ) WHERE matchcount = 2;
This is what I ended up using, inspired by #Leon Bambrick:
(Expanded a little to support multiple products in the criteria)
WITH cteUnionBase AS
(SELECT * FROM SalesDetails
WHERE Product IN ('Apple Red','Apple Yellow','Apple Green','Banana Small','Banana Large')),
cteBanana AS
(SELECT * FROM cteUnionBase
WHERE Product IN ('Banana Small','Banana Large')),
cteApple AS
(SELECT * FROM cteUnionBase
WHERE Product IN ('Apple Red','Apple Yellow','Apple Green')),
cteIntersect AS
(
SELECT InvoiceID FROM cteApple
Intersect
SELECT InvoiceID FROM cteBanana
)
SELECT cteUnionBase.*
FROM cteUnionBase INNER JOIN cteIntersect
on cteUnionBase.InvoiceID = cteIntersect.InvoiceID

Find Top Most AND Lowest In a Table's Group Column

I have a table and there are 4 fields in it, ID, Price, QTY, Ratting and Optional [Position].
I have all the records Grouped By Columns [Qty,Ratting]
I have to define the position of groupwise and store that Position into Optional column.
For better understanding I have added an image with data in table:
On the basis of QTY in Each Rating I have to Mark Top3, Bottom3 and Rest of them as remaining.
I am not getting how to do it.
Can anybody suggest me how to do it?
So far what I've tried is:
Declare #RankTable TABLE
(
ID INT,
Price Decimal (10,2),
Qty INT,
Ratting INT
)
INSERT INTO #RankTable
SELECT 1,10,15,1
UNION ALL
SELECT 2,11,11,1
UNION ALL
SELECT 3,96,10,1
UNION ALL
SELECT 4,96,8,1
UNION ALL
SELECT 5,56,7,1
UNION ALL
SELECT 6,74,5,1
UNION ALL
SELECT 7,93,4,1
UNION ALL
SELECT 8,98,2,1
UNION ALL
SELECT 9,12,1,1
UNION ALL
SELECT 10,32,80,2
UNION ALL
SELECT 11,74,68,2
UNION ALL
SELECT 12,58,57,2
UNION ALL
SELECT 13,37,43,2
UNION ALL
SELECT 14,79,32,2
UNION ALL
SELECT 15,29,28,2
UNION ALL
SELECT 16,46,17,2
UNION ALL
SELECT 17,86,13,2
UNION ALL
SELECT 19,75,110,3
UNION ALL
SELECT 20,27,108,3
UNION ALL
SELECT 21,38,104,3
UNION ALL
SELECT 22,87,100,3
UNION ALL
SELECT 23,47,89,3
DECLARE #PositionGroup VARCHAR(1)
SELECT *,ISNULL(#PositionGroup,'') AS Position FROM #RankTable
You can try this:
SELECT ID
,Price
,Qty
,Ratting
,CASE WHEN RowID >= 1 AND RowID <= 3
THEN 0
ELSE CASE WHEN RowID > Total - 3 THEN 1 ELSE 2 END END AS Position
FROM (SELECT ID
,Price
,Qty
,Ratting
,COUNT(*) OVER(PARTITION BY Ratting) AS Total
,ROW_NUMBER() OVER(PARTITION BY Ratting ORDER BY Qty DESC) AS RowID
,ISNULL(#PositionGroup,'') AS Position
FROM #RankTable) AS T
Use Window Function. Try this.
;WITH cte
AS (SELECT *,
Row_number()OVER(partition BY rating ORDER BY id) rn,
count(id)OVER(partition BY rating) mx
FROM #RankTable)
SELECT ID,
Price,
Qty,
Rating,
mx - rn,
CASE WHEN rn IN ( 1, 2, 3 ) THEN 0
WHEN mx - rn IN( 0, 1, 2 ) THEN 1
ELSE 2
END position
FROM cte
try this as well.
;WITH cte AS
(
SELECT MAX(Row) [Max],
MIN(Row) [Min],
LU.Ratting
FROM (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY Ratting ORDER BY Qty DESC) Row
FROM #RankTable)LU
GROUP BY LU.Ratting
)
SELECT ID,
R.Price,
R.Qty,
cte.Ratting,
CASE WHEN (Row - Min) <= 2 THEN 0 WHEN (Max - Row) <= 2 THEN 1 ELSE 2 END Position
FROM cte
JOIN (
SELECT Ratting,
ID,
Price,
Qty,
ROW_NUMBER() OVER(PARTITION BY Ratting ORDER BY Qty DESC) [Row]
FROM #RankTable
) R ON R.Ratting = cte.Ratting
Result: