Order result according to result of a function - sql-server

I use below query but occur error that
invalid column name money
select * from (SELECT ROW_NUMBER() OVER (order by money desc) as row,
ChargeLog.customerCode,(select taraz from getCustomerMoney(ChargeLog.customerCode)) as money
from ChargeLog
)tblTemp
WHERE row between (1 - 1) * 20 + 1 AND 1*20

Try this:
WITH T AS
( SELECT ChargeLog.customerCode,
(select taraz from getCustomerMoney(ChargeLog.customerCode)) as money
from ChargeLog
), T2 AS
(
SELECT ROW_NUMBER() OVER (order by money desc) as row,
customerCode,
Money
FROM T
)
SELECT * FROM T2 WHERE row between (1 - 1) * 20 + 1 AND 1*20

Related

T-SQL : how to split order detail quantities out into separate rows

I have following order detail query
OrderId item Quantity
----------------------------------
3402323 Item1 3
3402323 Item2 1
3402324 Item1 2
And the results I need are
OrderId item Quantity
----------------------------------
3402323-1 Item1 1
3402323-2 Item1 1
3402323-3 Item1 1
3402323-4 Item2 1
3402324-1 Item1 1
3402324-2 Item1 1
Is there a way to do this without using a temp table and populating it with a cursor?
I would personally use a Tally. These are far faster than a rCTE, especially if (in this scenario) you have large values for Quantity, and can't suffer from hitting the max recursion problem, as they aren't recursive.
WITH YourTable AS(
SELECT *
FROM (VALUES(3402323,'Item1',3),
(3402323,'Item2',1),
(3402324,'Item1',2))V(OrderID,Item,Quantity)),
N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT TOP (SELECT MAX(Quantity) FROM YourTable)
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1, N N2, N N3) --1000 rows
SELECT CONCAT(YT.OrderID,'-',T.I) AS OrderID,
YT.Item,
1 AS Quantity
FROM YourTable YT
JOIN Tally T ON YT.Quantity >= T.I;
Use a recursive subquery:
with cte as (
select orderid, item, quantity, 1 as n
from t
union all
select orderid, item, quantity, n + 1
from cte
where n < quantity
)
select orderid, item, 1 as quantity
from cte;
If your quantities exceed 100, then you need to add option (maxrecursion 0).
If you have SQL Server 2016 or later you can utilize the STRING_SPLIT function and the REPLICATE function to achieve the result you are looking for. The REPLICATE function can be used to create a comma delimited string that repeats N number of times (this will be your quantity). The STRING_SPLIT function can be used to split the string into N number of records you can join on.
The following example shows how this would work with your data.
DECLARE #orders TABLE
(
OrderId VARCHAR(50)
,Item VARCHAR(50)
,Quantity INT
)
INSERT INTO #orders
SELECT 3402323 ,'Item1', 3 UNION ALL
SELECT 3402323 ,'Item2', 1 UNION ALL
SELECT 3402324 ,'Item1', 2
SELECT
OrderId = O.OrderId + '-' + CAST( (ROW_NUMBER() OVER (PARTITION BY O.OrderId ORDER BY O.Item, OE.RepeatId) ) AS VARCHAR(50) )
,O.Item
,Quantity = 1
FROM
#orders O
CROSS APPLY
(
SELECT RepeatId = ROW_NUMBER() OVER (ORDER BY [value])
FROM
STRING_SPLIT(REPLICATE ( ',', O.Quantity - 1), ',')
) OE

Setting a column with a Page value for every million rows

Users
-RowID
-PageNumber
-...
For each 1 million ROWS, I want TO SET the value OF the PageNumber WITH an increment number.
So 1 to 1 000 000 will have a PageNumber of 1. Then from 1 000 001 to 2 000 000 will have a PageNumber of 2 and so on...
Just some math using a running row number.
SELECT
FLOOR((ROW_NUMBER() OVER (ORDER BY (SELECT 1)) + 999999) / 1000000)
FROM
YourTable;
You can make use of ntile function to get your expected output. First you need to set a static value of 1 million and then get the number of partitions required based on total count / total partitions.
Declare #valueset int = 1000000
Declare #totalcount bigint = (Select count(1) total from yourtable)
Declare #totalpartition int = (select ceiling(#totalcount*1.0/#valueset*1.0))
--select #totalpartition
select ntile(#totalpartition) over (order by somecolumn ) pagenumber,* from yourtable
As you confirmed RowId is a auto increment column, you can do this to update your new column-
SELECT:
SELECT *,
CASE
WHEN ROW_NUMBER() OVER(ORDER BY RowId) %100000 = 0
THEN ROW_NUMBER() OVER(ORDER BY RowId) /100000
ELSE ROW_NUMBER() OVER(ORDER BY RowId) /100000 + 1
END
FROM your_table
UPDATE:
WITH CTE AS (
SELECT RowId,
CASE
WHEN ROW_NUMBER() OVER(ORDER BY RowId) %100000 = 0
THEN ROW_NUMBER() OVER(ORDER BY RowId) /100000
ELSE ROW_NUMBER() OVER(ORDER BY RowId) /100000 + 1
END NV
FROM your_table
)
UPDATE A
SET A.NewColumnName = B.NV
FROM your_table A
INNER JOIN CTE B ON A.RowId= B.RowId
Caution: Please try with test data first.

Can the same query be done with Pivot?

I have a table as
CREATE TABLE #FinalRates
(
id int primary key identity(1,1),
RateDesc nvarchar(50),
Amt decimal(18,2)
)
insert into #FinalRates values('100',200)
insert into #FinalRates values('100',300)
insert into #FinalRates values('50-80',100)
insert into #FinalRates values('50-80',300)
insert into #FinalRates values('30-50',500)
insert into #FinalRates values('30-50',250)
Looking for an output as
RateDesc Amount1 Amount2
100 200 300
50-80 100 300
30-50 500 250
I have done this as
;with cte as(
select
RateDesc
,Amounts=
STUFF((Select ','+ cast(cast(Amt as int) as varchar(10))
from #FinalRates T1
where T1.RateDesc=T2.RateDesc
FOR XML PATH('')),1,1,'')
from #FinalRates T2
group by T2.RateDesc
)
select
RateDesc,
Amount1 = PARSENAME(REPLACE(Amounts,',','.'),2),
Amount2 = PARSENAME(REPLACE(Amounts,',','.'),1)
From Cte
Drop table #FinalRates
Can the same be done using PIVOT?
That's so complicated. How about this?
select ratedesc,
max(case when seqnum = 1 then amt end) as Amount1,
max(case when seqnum = 2 then amt end) as Amount2
from (select ft.*,
row_number() over (partition by ratedesc order by id) as seqnum
from #finalrates fr
) fr
group by ratedesc;
You could use a similar approach using pivot but conditional aggregation often performs better.
Plus, if you know you have no holes in id, you can do:
select ratedesc,
max(case when id % 2 = 1 then amt end) as Amount1,
max(case when id % 2 = 0 then amt end) as Amount2
from #finalrates fr
group by ratedesc;
Using PIVOT,
Assuming you have 2 Amt for each RateDesc.
Select RateDesc, [Amount1], [Amount2] From
(
Select RateDesc, Amt
, 'Amount' + cast(row_number() over (partition by RateDesc order by Amt) as varchar(5)) RowVal
from #FinalRates
) x
PIVOT
(
MAX(Amt) For RowVal in ([Amount1], [Amount2])
) p

Unexpected results with SELECT INTO

I've got a table valued function created in MSSQL that takes in 2 paramateres
1. End Date
2. Number of weeks to go back
to generate a table of dates with the week start date.
The PERIOD table simply is a table with 1 column (called pPeriod) with all the dates between '19971229' and '20201231'
CREATE FUNCTION [dbo].[Get_Week_Rank]
(
-- Add the parameters for the function here
#PERIOD_END DATETIME,
#NUM_WEEKS INT
)
RETURNS TABLE
AS
RETURN
(
SELECT A.PPERIOD TY_PPERIOD, B.PPERIOD TY_PWKSTART, DATEADD(YY, -1, A.PPERIOD) LY_PPERIOD, DATEADD(YY, -1, B.PPERIOD) LY_PWKSTART, B.CRNK WEEK_RANK FROM (
SELECT PPERIOD, ROW_NUMBER() OVER (ORDER BY PPERIOD) CRNK FROM PERIODS
WHERE PPERIOD BETWEEN DATEADD(WW, #NUM_WEEKS - 1, #PERIOD_END) + 1 AND #PERIOD_END
) AS A
JOIN (
SELECT PPERIOD, ROW_NUMBER() OVER (ORDER BY CRNK % 7) CRNK FROM (
SELECT PPERIOD, ROW_NUMBER() OVER (ORDER BY PPERIOD) CRNK FROM PERIODS
WHERE PPERIOD BETWEEN DATEADD(WW, #NUM_WEEKS, #PERIOD_END) + 1 AND #PERIOD_END
) AS A
WHERE CRNK % 7 = 1
) AS B ON (A.CRNK - 1)/7 = B.CRNK
)
I noticed then when #NUM_WEEKS is between -1 and -130, the results are correct when running this query:
SELECT * INTO #WEEKS FROM GET_WEEK_RANK('20160401', -104)
SELECT * FROM #WEEKS ORDER BY 1
However, any number below -130 (eg -156, -208), the returned results are all wrong.
Wrong results
You can see that TY_PWKSTART is all jumbled up and not in sync with TY_PPERIOD.
If I run the query directly, the results return fine:
SELECT * FROM GET_WEEK_RANK('20160401', -140)
What could be the issue? I am using Microsoft SQL Server 2014
EDIT: Posting images of results
As you can see, both queries essentially do the same thing, but the results returned are different. The order of pWkStart in the first query when using SELECT INTO is wrong.
Wrong results:
SELECT * INTO #WEEK_WRONG FROM GET_WEEK_RANK('20160410', -140)
SELECT * FROM #WEEK_WRONG ORDER BY 1
Correct results:
CREATE TABLE #WEEK_CORRECT (TY_PPERIOD DATETIME, TY_PWKSTART DATETIME, LY_PPERIOD DATETIME, LY_PWKTART DATETIME, WEEK_RANK INT)
INSERT INTO #WEEK_CORRECT
SELECT * FROM GET_WEEK_RANK('20160410', -140)
SELECT * FROM #WEEK_CORRECT ORDER BY 1
EDIT2:
Turns out that my initial query was producing unexpected results. I've fixed my query and managed to get consistent results from SELECT INTO and INSERT INTO.
Just sharing the code here:
CREATE FUNCTION [dbo].[Get_Week_Rank]
(
#PERIOD_END DATETIME,
#NUM_WEEKS INT
)
RETURNS TABLE
AS
RETURN
(
SELECT A.PPERIOD TY_PPERIOD, B.PPERIOD TY_PWKSTART, DATEADD(YY, -1, A.PPERIOD) LY_PPERIOD, DATEADD(YY, -1, B.PPERIOD) LY_PWKSTART, B.CRNK + 1 WEEK_RANK FROM (
SELECT PPERIOD, (ROW_NUMBER() OVER (ORDER BY PPERIOD)-1)/7 CRNK FROM PERIODS
WHERE PPERIOD BETWEEN DATEADD(WW, #NUM_WEEKS, #PERIOD_END) + 1 AND #PERIOD_END
) AS A
JOIN (
SELECT PPERIOD, ROW_NUMBER() OVER (ORDER BY PPERIOD)-1 CRNK FROM (
SELECT PPERIOD , ROW_NUMBER() OVER (PARTITION BY CRNK ORDER BY CRNK) CRNK FROM (
SELECT PPERIOD, (ROW_NUMBER() OVER (ORDER BY PPERIOD)-1)/7 CRNK FROM PERIODS
WHERE PPERIOD BETWEEN DATEADD(WW, #NUM_WEEKS, #PERIOD_END) + 1 AND #PERIOD_END
) AS A
) AS A
WHERE CRNK = 1
) AS B ON A.CRNK = B.CRNK
)
This part of your query is broken:
SELECT PPERIOD, ROW_NUMBER() OVER (ORDER BY CRNK % 7) CRNK FROM (
...
) AS A
WHERE CRNK % 7 = 1
Since the where clause establishes that CRNK % 7 is equal to 1, the ROW_NUMBER() expression is free to assign row numbers in any order1. I would guess that you still would want to assign row numbers in the order in which PPERIOD or CRNK values work, and so the expression should instead by:
SELECT PPERIOD, ROW_NUMBER() OVER (ORDER BY CRNK) CRNK FROM (
...
) AS A
WHERE CRNK % 7 = 1
1Since you haven't provided enough expressions in the ORDER BY for row numbers to be assigned unambiguously, there's no guarantee on the values assigned to each row.

Microsoft SQL Query - Return last record from second table

I have 2 tables which I am wanting to run a query on. The first table is dbo.Incidents which contacts the primary key IncidentID. The second table is dbo.IncidentActions which has the primary key ActionID and has a field IncidentID which links with the first table.
There are many actions with the same IncidentID and I am wanting to return 1 row only per IncidentID with the last ActionID for that IncidentID.
Thanks Andomar - nearly there I promise :)
select *
from (
select i.IncidentID
, ia.ActionID
, RIGHT('' + CAST(DATEDIFF(mi, ia.ActionTime, CONVERT([varchar], GETDATE(), 14))
/ 60 % 60 AS VARCHAR), 2) + ' hr(s) ' + RIGHT('' + CAST(DATEDIFF(mi, ia.ActionTime, CONVERT([varchar], GETDATE(), 14)) % 60 AS VARCHAR), 2) + ' min(s)' AS LastActionTime
, row_number() over (
partition by i.IncidentID
order by ia.ActionID desc) as rn
from dbo.Incident i
join dbo.IncidentAction ia
on i.IncidentID = ia.IncidentID
) as SubQueryAlias
where rn = 1
This is all working now I just want to set Where ia.ActionDate = GetDate() - can't seem to get that working
If you're just looking for the top ActionID per incident:
select i.IncidentID
, max(ia.ActionID) as MaxActionIdForIncident
from Incidents i
join IncidentActions ia
on i.IncidentID = ia.IncidentID
group by
i.IncidentID
If the IncidentActions table has a timestamp column you'd like to use to determine which row to return, you could use the row_number() window function:
select *
from (
select i.IncidentID
, ia.ActionID
, ia.ActionByUser -- Note: now you return any column
, row_number() over (
partition by i.IncidentID
order by ia.ActionTimestamp desc) as rn
from Incidents i
join IncidentActions ia
on i.IncidentID= ia.IncidentID
) as SubQueryAlias
where rn = 1 -- Latest action per incident only
The subquery is required because you can't use window functions in the where clause. For more examples, browse the greatest-n-per-group tag.

Resources