sql query issue: warehouse aging inventory when negative entries - sql

I found a warehouse aging inventory example online (see modified code below).
Everything works fine if the entry type for purchase (0) is positive and for sales (1) is negative. But if the values are inverse (because of cancellation) then the results will be wrong.
Example: There are four entries, three of them are purchase entries but as you can see the second one has been canceled that's why the quantity is negative.
The total sum of column RemainingQty must be 0 in that case but result is 1699.
What do I have to change in my SQL query?
Thanks for any advice.
DECLARE #ItemLedgerEntry TABLE
(
id INT IDENTITY(1, 1) NOT NULL PRIMARY KEY ,
ItemNo INT NOT NULL, --references another item
Qty FLOAT NOT NULL, --quantity
EntryType INT NOT NULL, --type=0 bought, type=1 sold
PostingDate DATETIME NOT NULL -- transaction date
);
INSERT #ItemLedgerEntry
( ItemNo, qty, EntryType, PostingDate )
VALUES ( 1999, 1700, 0, '10-06-2021'),
( 1999, -1700, 0, '29-06-2021'),
( 1999, 1, 0, '03-08-2021'),
( 1999, - 1, 1, '09-08-2021');
WITH Sold
AS ( SELECT IT.[ItemNo] ,
SUM(IT.Qty) AS TotalSoldQty
FROM #ItemLedgerEntry IT
WHERE It.[EntryType] =1
GROUP BY ItemNo
),
Bought
AS ( SELECT IT.* ,
(
SELECT SUM(RS.Qty)
FROM #ItemLedgerEntry RS
WHERE RS.[EntryType] =0 AND RS.[ItemNo] = IT.[ItemNo] AND RS.[PostingDate] <= IT.[PostingDate]
) AS RunningBoughtQty
FROM #ItemLedgerEntry IT
WHERE IT.[EntryType] = 0
)
SELECT
B.[ItemNo],
B.[PostingDate],
B.[EntryType],
S.TotalSoldQty,
B.RunningBoughtQty,
B.RunningBoughtQty + S.TotalSoldQty AS RunningDifferenceQty,
CASE WHEN (B.RunningBoughtQty) + (S.TotalSoldQty) <0
THEN 0
ELSE B.RunningBoughtQty + S.TotalSoldQty
END AS RunningRemainingQty,
CASE WHEN B.RunningBoughtQty + S.TotalSoldQty < 0 THEN 0
WHEN B.RunningBoughtQty + S.TotalSoldQty > B.Qty THEN B.Qty
ELSE B.RunningBoughtQty + S.TotalSoldQty
END AS RemainingQty
FROM Bought B
inner JOIN Sold S ON B.[ItemNo] = S.[ItemNo]

Related

Return columns from different tables that are after a given date

I need to write a query that returns product orders that were open during April of 2018 and are still open and also returns product orders that were open during April of 2018 and are no longer open.
The rows need to include in the results of the name of the customer that placed the order, the id for the order, and the date the order was filled.
Here is the table info
CREATE TABLE dbo.ProductOrders
(
POID INT NOT NULL IDENTITY(1, 1) PRIMARY KEY ,
ProductId INT NOT NULL
CONSTRAINT FK_ProductOrders_ProductId_ref_Products_ProductId
FOREIGN KEY REFERENCES dbo.Products ( ProductId ) ,
CustomerId INT NOT NULL ,
OrderedQuantity INT ,
Filled BIT NOT NULL
CONSTRAINT DF_ProductOrders_Filled
DEFAULT ( 0 ) ,
DateOrdered DATETIME
CONSTRAINT DF_ProductOrders_DateOrdered
DEFAULT ( GETDATE()) ,
DateFilled DATETIME
CONSTRAINT DF_ProductOrders_DateFilled
DEFAULT ( GETDATE())
);
INSERT dbo.ProductOrders ( ProductId ,
CustomerId ,
OrderedQuantity ,
Filled ,
DateOrdered ,
DateFilled )
VALUES ( 2, 1, 1000, 0, '4/16/18 8:09:13', NULL ) ,
( 2, 1, 500, 1, '3/27/18 17:00:21', '6/24/18 13:29:01' ) ,
( 3, 3, 2000, 1, '12/01/04 13:28:58', '2/19/05 19:41:42' ) ,
( 1, 1, 632, 0, '5/23/18 4:25:52', NULL ) ,
( 4, 4, 901, 0, '3/30/18 21:30:28', NULL );
CREATE TABLE dbo.Customers
(
CustomerId INT NOT NULL IDENTITY(1, 1) PRIMARY KEY ,
CustomerName NVARCHAR(100) ,
Active BIT NOT NULL
CONSTRAINT DF_Customers_Active
DEFAULT ( 1 )
);
INSERT dbo.Customers ( CustomerName ,
Active )
VALUES ( 'Bikes R'' Us', 1 ) ,
( 'Industrial Giant', 1 ) ,
( 'Widget-Works', 0 ) ,
( 'Custom Hangers', 1 );
This is my best attempt at it, I know this is not the right syntax but I'm not sure if I need a join between these to tables to make this work or how I would go about selecting orders that start at April 2018 and also are open or closed after that date.
select CustomerName, POID, DataFilled,
From ProductOrders, Customers
Where DateOrdered is >= April 2018
I think you want and join and filtering:
select c.customername, po.poid, po.dateordered, po.datefilled
from productorders po
inner join customers c on c.customerid = po.customerid
where
po.dateordered >= '20180401'
and po.dateordered < '20180501'
and po.datefilled < getdate()
This gives you orders that were ordered in April 2018 and are not open anymore as of now. To get orders that are still open, you would change the last condition to po.datefilled is null.

dynamic grouping by in sql server

i want to make a stored procedure like makeGroupBy(#a int,#b int,#c int). The inputs are 0 or 1 to decide which columns to group by. My attempt so far is below:
-- exec makeGroupBy 0,1,0
-- exec makeGroupBy 0,1,1
create proc makeGroupBy(#product_id int = 0,#city_id int = 1,#date_key
int = 0) as
begin
declare #tbl as table(product_id int, city_id int, date_key int, amount
float)
insert into #tbl
values(1,1,1,10),
(1,1,1,10),
(1,2,1,5),
(2,2,3,15),
(2,1,3,20),
(3,1,1,25)
select case isnull(#product_id,0) when 0 then 0 else product_id end
,case isnull(#city_id,0) when 0 then 0 else city_id end
,case isnull(#date_key,0) when 0 then 0 else date_key end
, sum(amount) amount from #tbl
group by case isnull(#product_id,0) when 0 then 0 else product_id end
,case isnull(#city_id,0) when 0 then 0 else city_id end
,case isnull(#date_key,0) when 0 then 0 else date_key end
end
I don't know if it's possible but what I want is to omit the unwanted columns (inputs with value 0) in the result set.
Assuming that your sql-server version is greater or equal than 2008.
select
product_id
,city_id
,date_key
,sum(amount) as total_amount
from #tbl
group by grouping sets (
(product_id, city_id, date_key)
, (product_id,city_id)
, (product_id, date_key)
, (city_id, date_key)
, (product_id)
, (city_id)
, (date_key))
having concat(iif(grouping_id(product_id)=0,1,0),iif(grouping_id(city_id)=0,1,0),iif(grouping_id(date_key)=0,1,0)) = concat(#product_id, #city_id, #date_key)
order by concat(iif(grouping_id(product_id)=0,1,0),iif(grouping_id(city_id)=0,1,0),iif(grouping_id(date_key)=0,1,0))
It seems that a view will probably suit best for this case
create view [view_name]
as
select
product_id
,city_id
,date_key
,sum(amount) as amount
,concat(iif(grouping_id(product_id)=0,1,0),iif(grouping_id(city_id)=0,1,0),iif(grouping_id(date_key)=0,1,0)) as grp_key
from #tbl
group by grouping sets (
(product_id, city_id, date_key)
, (product_id,city_id)
, (product_id, date_key)
, (city_id, date_key)
, (product_id)
, (city_id)
, (date_key))
go
Then you can query the view like
select
city_id
,date_key
,amount
from [view_name]
where grp_key = concat(0,1,1)

SQL - Split values from one column into two separate columns

I am trying to take the value of column Quantity and split the values where Quantity is less than 500 and put those records into the LessThan500 column, for the records that are greater than 500 into the GreaterThan500 column.
I have a select statement that does this, but the table does not get updated properly
SUM(CASE WHEN Quantity < 500 THEN CAST(( Quantity*10 ) AS INT)
ELSE ''
END) as Quantityx10
, SUM(CASE WHEN Quantity < 500 THEN CAST(( Quantity*10 ) AS INT)
ELSE ''
END) AS '<500'
, '' AS '>=500'
USE FET
-- Create a new template table
IF OBJECT_ID('dbo.FuelSales', 'U') IS NOT NULL
DROP TABLE dbo.FuelSales;
CREATE TABLE FuelSales
(
TransactionType varchar(1) null,
TransactionID int,
CustomerID int null,
TransactionDate date null,
EntryTimeofDay datetime null,
UserTranNumber varchar(15) null,
AircraftNumber varchar(10) null,
CompanyName varchar(40) null,
NumNameCode varchar(30) null,
Description varchar(40) null,
Quantity decimal(18,6) null,
LessThan500 decimal(18, 6) null,
GreaterThan500 decimal(18,6) null,
);
INSERT INTO FuelSales(TransactionType, TransactionID, CustomerID, TransactionDate, EntryTimeofDay,UserTranNumber,AircraftNumber,CompanyName,NumNameCode,Description,Quantity,LessThan500,GreaterThan500)
SELECT ci.TransactionType
, ci.TransactionID
, ci.CustomerID
, ci.TransactionDate
, ci.EntryTimeofDay
, ci.UserTranNumber
, d.AircraftNumber
, c.CompanyName
, d.NumNameCode
, d.Description
, Quantity
FROM [TFBO7].[dbo].[CustInv] ci
INNER JOIN [TFBO7].[dbo].[Cust] c
ON c.CustomerID=ci.CustomerID
INNER JOIN [TFBO7].[dbo].[CustIDet] d
ON ci.TransactionID=d.TransactionID
WHERE ci.TransactionDate between '20180701' and '20180731' and d.TransactionTypeID='1'
I'm not sure why would you do this permanently on the table and not just do the separation as an ad hoc query when needed but assuming it's SQL Server it would go something like this (in the INSERT statement you're already doing):
...
, Quantity
-- 500 has to go somewhere so I put it in the first category
, LessThan500 = CASE WHEN Quantity <= 500 THEN Quantity ELSE NULL END
, GreaterThan500 = CASE WHEN Quantity > 500 THEN Quantity ELSE NULL END
FROM [TFBO7].[dbo].[CustInv] ci
...
You can add a computed column to the table. Just do:
alter table fuelsales add lessthan500 as (case when quantity <= 500 then quantity end);
alter table fuelsales add greaterthan500 as (case when quantity > 500 then quantity end);
This column will appear in the table when you query the table. However, it is not stored and it is "updated" automatically when quantity changes.

Opening and Closing Balance Sum

I have this table and sample data and I want to calculate the opening and closing balance. I want it to be in 6 column
i.e CreditOpening, DebitOpening, Credit, Debit, CreditClosing, DebitClosing
Here is the table structure and sample data
DROP TABLE Transactions;
CREATE TABLE Transactions
(
ID INT,
COATitle VARCHAR(35),
ConfigurationCode INT,
DebitAmount NUMERIC,
CreditAmount NUMERIC,
TransactionDate Date
)
INSERT INTO Transactions VALUES (1, 'Sales', 24, '2400', NULL, '2018-08-24');
INSERT INTO Transactions VALUES (2, 'Items', 24, NULL, '1200', '2018-08-24');
INSERT INTO Transactions VALUES (3, 'Bank', 24, NULL, '1200', '2018-08-24');
INSERT INTO Transactions VALUES (4, 'Meezan', 24, '1500', NULL, '2018-08-25');
INSERT INTO Transactions VALUES (5, 'Items', 24, NULL, '1500', '2018-08-25');
INSERT INTO Transactions VALUES (6, 'Bank', 24, NULL, '1200', '2018-08-26');
INSERT INTO Transactions VALUES (7, 'Sales', 24, '5400', NULL, '2018-08-26');
INSERT INTO Transactions VALUES (8, 'Items', 24, NULL, '1200', '2018-08-26');
INSERT INTO Transactions VALUES (9, 'Bank', 24, NULL, '3000', '2018-08-26');
I have this query and it's output as below:
;WITH CTE AS (
SELECT *
FROM
Transactions
)
SELECT
COATitle, SUM([D].[DebitAmount]) DrAmount, SUM([D].[CreditAmount]) CrAmount
FROM(
SELECT *,
SUM(ISNULL(NULLIF(DebitAmount, 0), 0)-ISNULL(NULLIF(CreditAmount, 0), 0)) OVER (PARTITION BY CONFIGURATIONCODE ORDER BY ID) as Amount
FROM CTE
WHERE [TransactionDate] BETWEEN CAST('Aug 25 2018 11:21AM' AS DATE) AND CAST('Aug 25 2018 11:21AM' AS DATE)
)D
GROUP BY COATitle
OutPut :
COATitle DrAmount CrAmount
Items NULL 1500
Meezan 1500 NULL
Now the data should look like this
COATitle OpeningDebit OpeningDebit DrAmount CrAmount ClosingDebit ClosingCredit
Bank 0 0 NULL 1200 0 1200
Items 0 0 NULL 1200 0 1200
Sales 0 0 2400 NULL 2400 0
But once I run the query between dates 25 and 26 the result should be something like this
COATitle OpeningDebit OpeningCredit DrAmount CrAmount ClosingDebit ClosingCredit
Bank 0 1200 NULL 4200 0 5400
Items 0 1200 NULL 2700 0 3900
Sales 0 0 5400 NULL 7800 0
Meezan 0 0 1500 NULL 1500 0
Meezan will not have opening balance as there was no in previous date. Now in case if there is Debit amount is given for any COATitle which was given Credit in previous dates it will subtract Credit From Debit.
You don't need the CTE in this case. It is redundant.
You don't need to convert the date in string to date data type. Just specify the date in ISO format YYYY-MM-DD will do.
I am may be wrong but your expected data does not matches with the sample data.
Note : i excluded the ConfigurationCode in the query as i am not sure how that column play a part in your requirement.
DECLARE #Date_Fr DATE = '2018-08-25',
#Date_To DATE = '2018-08-25'
SELECT t.COATitle,
OpeningDebit = ISNULL(o.OpeningDebit, 0),
OpeningCredit = ISNULL(o.OpeningCredit, 0),
t.DrAmount, t.CrAmount,
ClosingDebit = ISNULL(o.OpeningDebit, 0) + t.DrAmount,
ClosingCredit = ISNULL(o.OpeningCredit, 0) + t.CrAmount
FROM (
SELECT COATitle,
DrAmount = SUM(CASE WHEN [TransactionDate] >= #Date_Fr THEN DebitAmount ELSE 0 END),
CrAmount = SUM(CASE WHEN [TransactionDate] >= #Date_Fr THEN CreditAmount ELSE 0 END)
FROM Transactions t
WHERE [TransactionDate] <= #Date_To
GROUP BY COATitle
) t
OUTER APPLY
(
SELECT OpeningDebit = SUM(DebitAmount), OpeningCredit = SUM(CreditAmount)
FROM Transactions x
WHERE x.COATitle = t.COATitle
AND x.[TransactionDate] < #Date_Fr
) o
WHERE o.OpeningDebit IS NOT NULL
OR o.OpeningCredit iS NOT NULL
This may help. I put additional check, because I don't know if situation when opening balance is debit and closing is credit for example, is valid.
DECLARE
#from date = '25-08-2018',
#to date = '26-08-2018'
;
WITH Items (COATitle) AS (
SELECT DISTINCT COATitle
FROM dbo.[Transactions]
), OpeningBalance (COATitle, OpeningAmount) AS (
SELECT COATitle, SUM(ISNULL(DebitAmount, 0)) - SUM(ISNULL(CreditAmount, 0))
FROM dbo.[Transactions]
WHERE TransactionDate < #from
GROUP BY COATitle
), DebitCredit(COATitle, DebitAmount, CreditAmount) AS (
SELECT COATitle, SUM(ISNULL(DebitAmount, 0)), SUM(ISNULL(CreditAmount, 0))
FROM dbo.[Transactions]
WHERE (#from <= TransactionDate) AND (TransactionDate <= #to)
GROUP BY COATitle
)
SELECT
i.COATitle,
OpeningDebitAmount = (CASE WHEN SUM(ISNULL(ob.OpeningAmount, 0)) < 0 THEN 0 ELSE SUM(ISNULL(ob.OpeningAmount, 0)) END),
OpeningCreditAmount = (CASE WHEN SUM(ISNULL(ob.OpeningAmount, 0)) < 0 THEN -SUM(ISNULL(ob.OpeningAmount, 0)) ELSE 0 END),
DebitAmount = SUM(ISNULL(dc.DebitAmount, 0)),
CreditAmount = SUM(ISNULL(dc.CreditAmount, 0)),
ClosingDebitAmount = (CASE WHEN SUM(ISNULL(ob.OpeningAmount,0)+ISNULL(dc.DebitAmount,0)-ISNULL(dc.CreditAmount, 0)) < 0 THEN 0 ELSE SUM(ISNULL(ob.OpeningAmount,0)+ISNULL(dc.DebitAmount,0)-ISNULL(dc.CreditAmount, 0)) END),
ClosingCreditAmount = (CASE WHEN SUM(ISNULL(ob.OpeningAmount,0)+ISNULL(dc.DebitAmount,0)-ISNULL(dc.CreditAmount, 0)) < 0 THEN -SUM(ISNULL(ob.OpeningAmount,0)+ISNULL(dc.DebitAmount,0)-ISNULL(dc.CreditAmount, 0)) ELSE 0 END)
FROM Items i
LEFT JOIN OpeningBalance ob ON (i.COATitle = ob.COATitle)
LEFT JOIN DebitCredit dc ON (i.COATitle = dc.COATitle)
GROUP BY i.COATitle
Here's a possible solution for you. First, sample data:
declare #Transactions table
(
ID int,
COATitle varchar(35),
ConfigurationCode int,
DebitAmount money,
CreditAmount money,
TransactionDate date
);
insert #Transactions values
(1, 'Sales', 24, 2400, NULL, '20180824'),
(2, 'Items', 24, NULL, 1200, '20180824'),
(3, 'Bank', 24, NULL, 1200, '20180824'),
(4, 'Meezan', 24, 1500, NULL, '20180825'),
(5, 'Items', 24, NULL, 1500, '20180825'),
(6, 'Bank', 24, NULL, 1200, '20180826'),
(7, 'Sales', 24, 5400, NULL, '20180826'),
(8, 'Items', 24, NULL, 1200, '20180826'),
(9, 'Bank', 24, NULL, 3000, '20180826');
A couple of things to note here. There's no need to use quotes to delimit numeric literals as you've done in the original question, and I would generally advise against using the numeric data type without specifying the precision and scale explicitly. I've chosen the money type for debit and credit amounts instead.
Next, I'll use a couple of local variables to control the operation of the query. For your first test case we'll use:
declare #BeginDate date = '20180801';
declare #EndDate date = '20180824';
Here's the implementation:
with RawDataCTE as
(
select
T.COATitle,
OpeningDebit = coalesce(sum(case when T.TransactionDate < #BeginDate then T.DebitAmount end), 0),
OpeningCredit = coalesce(sum(case when T.TransactionDate < #BeginDate then T.CreditAmount end), 0),
DrAmount = sum(case when T.TransactionDate between #BeginDate and #EndDate then T.DebitAmount end),
CrAmount = sum(case when T.TransactionDate between #BeginDate and #EndDate then T.CreditAmount end)
from
#Transactions T
group by
T.COATitle
)
select
R.COATitle,
R.OpeningDebit,
R.OpeningCredit,
R.DrAmount,
R.CrAmount,
ClosingDebit = R.OpeningDebit + coalesce(R.DrAmount, 0),
ClosingCredit = R.OpeningCredit + coalesce(R.CrAmount, 0)
from
RawDataCTE R
where
R.OpeningDebit > 0 or
R.OpeningCredit > 0 or
R.DrAmount > 0 or
R.CrAmount > 0;
The CTE groups everything by COATitle, and as I've assumed from your desired results, produces non-null results for the opening balances but may produce null results for the sum of debits and credits that fall within the desired time frame. The CTE doesn't try to decide which COATitle records should be included or excluded because we need to have the data aggregated before making that decision.
The query outside the CTE builds the closing balances from the CTE data and omits any COATitle for which there are no opening balances and no entries made during the time period (and therefore no closing balances). When I run the query for 2018-08-01 through 2018-08-24, the result is:
And for 2018-08-25 through 2018-08-26, the result set is:
Technically you could do this without a CTE if you wanted; you'd just have to use a HAVING clause instead of a WHERE to determine which accounts are to be included, and you'd have to define ClosingDebit and ClosingCredit independently rather than as sums of the other fields in the result set. For instance, this will work:
select
T.COATitle,
OpeningDebit = coalesce(sum(case when T.TransactionDate < #BeginDate then T.DebitAmount end), 0),
OpeningCredit = coalesce(sum(case when T.TransactionDate < #BeginDate then T.CreditAmount end), 0),
DrAmount = sum(case when T.TransactionDate between #BeginDate and #EndDate then T.DebitAmount end),
CrAmount = sum(case when T.TransactionDate between #BeginDate and #EndDate then T.CreditAmount end),
ClosingDebit = coalesce(sum(case when T.TransactionDate <= #EndDate then T.DebitAmount end), 0),
ClosingCredit = coalesce(sum(case when T.TransactionDate <= #EndDate then T.CreditAmount end), 0)
from
#Transactions T
group by
T.COATitle
having
sum(case when T.TransactionDate <= #EndDate and (T.DebitAmount > 0 or T.CreditAmount > 0) then 1 else 0 end) > 0;
I find the CTE version a little easier to read and understand, but your mileage may vary.

SQL number greater than select results

I'm struggling to think of a way to do this with T-SQL.
I have a table which is populated every 5 seconds with the prices of three currencies (GBP, EUR & USD)
I've created a trigger (after insert), which selects the last 5 records entered for a given currency:
SELECT TOP 5 Price from dbo.prices where coin='GBP' ORDER BY Date Desc
I want to determine if the last inserted currency price is greater than the selected 5 above, how do i do this?
Thanks
As I guess: there cant be two entries for the same currency at one time. Only one insert per currency per some time (5sec). So this should fit yours requirements:
declare #prices table ([Date] int IDENTITY(1,1) primary key, Price float, coin varchar(3));
insert into #prices (coin, Price) values
('GBP', 3.20),('EUR', 3.14),('USD', 3.14),
('GBP', 3.17),('EUR', 3.16),('USD', 3.11),
('GBP', 3.14),('EUR', 3.13),('USD', 3.16),
('GBP', 3.15),('EUR', 3.12),('USD', 3.17),
('GBP', 3.16),('EUR', 3.17),('USD', 3.11),
('GBP', 3.15),('EUR', 3.14),('USD', 3.12),
('GBP', 3.19),('EUR', 3.14),('USD', 3.16)
select
case
when NEW.Price > PREV.Price Then 'yes'
else 'No'
end as CURR_JUMP_UP
from
(
select top 1 COALESCE(Price,0) Price, [Date]
from #prices where coin='GBP' order by [Date] desc
) NEW
cross apply
(
select MAX(Price) Price from
(
select top 5 Price
from #prices
where coin='GBP' and [Date]<NEW.[Date]
order by [Date] desc
) t
) PREV
Try this query:
DECLARE #AmountLastFiveEntry DECIMAL= (SELECT TOP 5 SUM(Price) FROM dbo.prices WHERE
ID NOT IN (SELECT TOP 1 ID
FROM dbo.prices where coin='GBP' ORDER BY Date Desc) where coin='GBP' ORDER BY Date Desc)
IF #AmountLastFiveEntry<(SELECT TOP 1 Price
FROM dbo.prices where coin='GBP' ORDER BY Date Desc)
BEGIN
SELECT #AmountLastFiveEntry --To do task
END
Trigger part is confusing
This will report if the latest price is higher (or equal) to the largest of the prior 5.
declare #currency table (iden int IDENTITY(1,1) primary key, exchange smallint, coin tinyint);
insert into #currency (coin, exchange) values
(1, 1)
, (1, 2)
, (1, 3)
, (1, 4)
, (1, 5)
, (1, 6)
, (2, 1)
, (2, 2)
, (2, 3)
, (2, 4)
, (2, 5)
, (2, 3);
select cccc.coin, cccc.exchange
, case when cccc.rn = cccc.rne then 'yes'
else 'no'
end as 'high'
from ( select ccc.iden, ccc.coin, ccc.exchange, ccc.rn
, ROW_NUMBER() over (partition by ccc.coin order by ccc.exchange desc, ccc.rn) rne
from ( select cc.iden, cc.coin, cc.exchange, cc.rn
from ( select c.iden, c.coin, c.exchange
, ROW_NUMBER() over (partition by coin order by iden desc) as rn
from #currency c
) cc
where cc.rn <= 6
) ccc
) cccc
where cccc.rn = 1
order by cccc.coin