How do I get the 777777 from the last record found in the order by id
SELECT
STRING_AGG(name,'-') WITHIN GROUP (ORDER BY id) as names,
STRING_AGG([numerToCall],'-') as calledNumbers,
--LAST_VALUE([numerToCall])) OVER (ORDER BY id) as lastCalled
'777777' as lastCalled
FROM
SimpleTest1
to create table and data
CREATE TABLE [dbo].[simpleTest1](
[id] [int] IDENTITY(1,1) NOT NULL,
[name] [varchar](50) NOT NULL,
[numerToCall] [varchar](50) NOT NULL,
CONSTRAINT [PK_simpleTest1] PRIMARY KEY CLUSTERED
(
[id] ASC
)
) ON [PRIMARY]
SET IDENTITY_INSERT [dbo].[simpleTest1] ON
INSERT [dbo].[simpleTest1] ([id], [name], [numerToCall]) VALUES (1, N'benny', N'555555')
INSERT [dbo].[simpleTest1] ([id], [name], [numerToCall]) VALUES (2, N'helle', N'999999')
INSERT [dbo].[simpleTest1] ([id], [name], [numerToCall]) VALUES (3, N'hans', N'777777')
SET IDENTITY_INSERT [dbo].[simpleTest1] OFF
The solution was
SELECT
STRING_AGG(t.name,'-') WITHIN GROUP (ORDER BY id) as names,
STRING_AGG(t.numerToCall, '-') as calledNumbers,
MIN(t.xxxx) as lastCalled
FROM (
SELECT *,
LAST_VALUE(t.numerToCall) OVER (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) as xxxx
from
SimpleTest1 t
) t;
thanks #Charlieface
You can put the window function in a derived table, and then use an aggregation function on it (either MIN or MAX)
SELECT
STRING_AGG(t.name,'-') WITHIN GROUP (ORDER BY id) as names,
STRING_AGG(t.numerToCall, '-') WITHIN GROUP (ORDER BY id) as calledNumbers,
MIN(t.numerToCall]) as lastCalled
FROM (
SELECT *,
LAST_VALUE(t.numerToCall) OVER (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) as lastCalled
SimpleTest1 t
) t;
You can use sub-query
SELECT
STRING_AGG(t.name,'-') WITHIN GROUP (ORDER BY id) as names,
STRING_AGG(t.[numerToCall],'-') WITHIN GROUP (ORDER BY id) as calledNumbers,
--LAST_VALUE([numerToCall])) OVER (ORDER BY id) as lastCalled
(SELECT TOP 1 InTbl.numerToCall FROM SimpleTest1 InTbl ORDER BY id desc) as lastCalled
FROM
SimpleTest1 t
GO
Or you can parse the result of the aggregation and get the result from the string without the need of another query (no sub-query)
SELECT
STRING_AGG(t.name,'-') WITHIN GROUP (ORDER BY id) as names,
STRING_AGG(t.[numerToCall],'-') WITHIN GROUP (ORDER BY id) as calledNumbers,
--LAST_VALUE([numerToCall])) OVER (ORDER BY id) as lastCalled
RIGHT(STRING_AGG(t.[numerToCall],'-') WITHIN GROUP (ORDER BY id), CHARINDEX('-',REVERSE(STRING_AGG(t.[numerToCall],'-') WITHIN GROUP (ORDER BY id)))-1) as lastCalled
FROM
SimpleTest1 t
GO
Check both solutions in your specific database and choose the one which provide you better performance (SSMS execution Plan analyze shows that second query is better but don't count on it and check IO and TIME in your server)
Related
I am new to MSSQL and I have collided with a wall here, what I am trying to do is to get the maximum value after summing and grouping up the value of the attribute "Quantity". After executing the query I have written below, I get this error "Incorrect syntax near ')'."
Select BookID, Sum(OrderMem.Quantity)
from OrderMem
Group By BookID
Having Sum(OrderMem.Quantity) = (select MAX(Quantity)
from (Select Sum(OrderMem.Quantity)
from OrderMem
group by BookID
)
);
*The table involved is named [OrderMem]
Please help and thanks :)
As it is mentioned in the comments, you forgot to alias Sum(OrderMem.Quantity) as Quantity in the inner most subquery.
But you can simplify your query by sorting the resuts descending and getting only the top row(s):
select top 1 with ties
BookID, Sum(OrderMem.Quantity) qty
from OrderMem
group by BookID
order by qty desc
If you're at SQL server >= 2016 then you can use a common table expression to simplify things
DECLARE #OrderMem table (
BookID int
,
Quantity int
);
INSERT INTO #OrderMem
VALUES
(1, 3),
(1, 2),
(2, 4),
(2, 5),
(3, 3),
(3, 2),
(3, 1)
;WITH
CTE
AS
(
SELECT BookID, SUM(Quantity) Quantity
FROM #OrderMem OrderMem
GROUP BY BookID
)
SELECT TOP 1
BookID, Quantity
FROM CTE
ORDER BY Quantity DESC
Alternatively, as somebody already mentioned, you'd need to alias a column and a result of a select.
select BookID, Sum(OrderMem.Quantity)
from OrderMem
Group By BookID
Having Sum(OrderMem.Quantity) in (select MAX(a.Quantity)
from (Select Sum(OrderMem.Quantity) Quantity
from OrderMem
group by BookID
) a
);
I like to group my table by [ID] while using SUM and also bring back
[Product_Name] of the top ROW_NUMBER - not sure if I should use ROW_NUMBER, GROUPING SETS or loop through everything with FETCH... this is what I tried:
DECLARE #SampleTable TABLE
(
[ID] INT,
[Price] MONEY,
[Product_Name] VARCHAR(50)
)
INSERT INTO #SampleTable
VALUES (1, 100, 'Product_1'), (1, 200, 'Product_2'),
(1, 300, 'Product_3'), (2, 500, 'Product_4'),
(2, 200, 'Product_5'), (2, 300, 'Product_6');
SELECT
[ID],
[Product_Name],
[Price],
SUM([Price]) OVER (PARTITION BY [ID]) AS [Price_Total],
ROW_NUMBER() OVER (PARTITION BY [ID] ORDER BY [ID]) AS [Row_Number]
FROM
#SampleTable T1
My desired results - only two records:
1 Product_1 100.00 600.00 1
2 Product_4 500.00 1000.00 1
Any help or guidance is highly appreciated.
UPDATE:
I end up using what Prateek Sharma suggested in his comment, to simply wrap the query with another SELECT WHERE [Row_Number] = 1
SELECT * FROM
(
SELECT
[ID]
,[Product_Name]
,[Price]
,SUM([Price]) OVER (PARTITION BY [ID]) AS [Price_Total]
,ROW_NUMBER() OVER (PARTITION BY [ID] ORDER BY [ID]) AS [Row_Number]
FROM #SampleTable
) MultipleRows
WHERE [Row_Number] = 1
You should have a column on which you will perform ORDER BY for ROW_NUMBER(). In this case if you want to only rely on the table self index then it's OK to use ID column for ORDER BY.
Hence your query is correct and you can go with it.
Other option is to use WITH TIES clause. BUT again, If you will use WITH TIES clause with the ORDER BY on ID column then performance will be very poor. WITH TIES only performs well if you have well defined index. And, then can use that indexed column with WITH TIES clause.
SELECT TOP 1 WITH TIES *
FROM (
SELECT [ID]
,[Product_Name]
,[Price]
,SUM([Price]) OVER (PARTITION BY [ID]) AS [Price_Total]
FROM #SampleTable
) TAB
ORDER BY ROW_NUMBER() OVER (PARTITION BY [ID] ORDER BY <IndexedColumn> DESC)
This query may help you bit. But remember, it is also not going to provide better performance than the query written by you. It is only reducing the line of code.
One option is using the WITH TIES clause. No extra field RN.
Hopefully, you have a proper sequence number or date which can be used in either the sum() over or in the final row_number() over
Example
SELECT Top 1 with ties *
From (
Select [ID]
,[Product_Name]
,[Price]
,SUM([Price]) OVER (PARTITION BY [ID]) AS [Price_Total]
FROM #SampleTable T1
) A
Order By ROW_NUMBER() OVER (PARTITION BY [ID] ORDER BY [Price_Total] Desc)
Returns
ID Product_Name Price Price_Total
1 Product_1 100.00 600.00
2 Product_4 500.00 1000.00
There is no "top ROW_NUMBER" unless you have a column that defines ordering.
If you just want an arbitary row per id you can use the below. To deterministically pick one you would need to order by deterministic unique criteria.
DECLARE #SampleTable TABLE
(
ID INT,
Price MONEY,
Product_Name VARCHAR(50),
INDEX cix CLUSTERED (ID)
);
INSERT INTO #SampleTable
VALUES (1,100,'Product_1'),
(1,200,'Product_2'),
(1,300,'Product_3'),
(2,500,'Product_4'),
(2,200,'Product_5'),
(2,300,'Product_6');
WITH T AS
(
SELECT *,
OrderingColumn = ROW_NUMBER() OVER (ORDER BY (SELECT 0))
FROM #SampleTable
)
SELECT ID,
SUBSTRING(MIN(CONCAT(STR(OrderingColumn), Product_Name)), 11, 50) AS Product_Name,
CAST(SUBSTRING(MIN(CONCAT(STR(OrderingColumn), Price)), 11, 50) AS MONEY) AS Price,
SUM(Price) AS Price_Total
FROM T
GROUP BY ID
The plan for this is pretty efficient as it is able to use the index ordered by id and has no additional sorts, spools, or passes through the table.
I have a table with IDs and Items where sometimes the associated Item has a variation from the other Items associated with the same ID. I need a query that selects the most common Item and assigns it to that ID.
The below query works, but I'm hoping to optimize it to avoid having to join two separate CTEs at the end, and rather have one slick SELECT statement:
IF OBJECT_ID('tempdb..#Test') IS NOT NULL
DROP TABLE #Test
CREATE TABLE #Test
(
[ID] INT
,[Item] VARCHAR(20)
)
INSERT #Test
VALUES
(100, 'Apple'),
(100, 'Apple'),
(100, 'Apples'),
(200, 'Orange'),
(200, 'Orange'),
(200, 'Orange'),
(200, 'Oranges'),
(300, 'Grape');
WITH cteOne AS (SELECT
[ID]
,[Item]
,COUNT(*) [Count]
FROM #Test
GROUP BY [ID]
,[Item]
),
cteTwo AS (SELECT
[ID]
,MAX([Count]) [Max]
FROM cteOne
GROUP BY [ID])
SELECT
C1.[ID]
,C1.[Item]
FROM cteOne C1
INNER JOIN cteTwo C2 ON C2.[ID] = C1.[ID]
AND C2.[Max] = C1.[Count]
ORDER BY [ID]
Any help is appreciated!
You can try top 1 with ties with row_number
select
top 1 with ties [ID], [Item]
from (
SELECT
[ID], [Item], COUNT(*) [Count]
FROM #Test
GROUP BY [ID], [Item]
) t
order by row_number() over (partition by [ID] order by [Count] desc)
This is even better:
;WITH
cteOne AS (
SELECT [ID],[Item] ,COUNT(*) [Count]
FROM #Test
GROUP BY [ID],[Item]
),
cteTwoo as (
select *, ROW_NUMBER() over (partition by id order by count) idx
from cteOne
)
select ID, Item
from cteTwoo
where idx = 1
I am trying to update DaysInPeriod with the DateDiff function, based on the change in EFFECTIVESTARTDATE field.
Here is my DLL:
DROP TABLE Reporting_Table
CREATE TABLE Reporting_Table (
Credit_Line_NO Varchar(10),
CURRENCY VARCHAR(3),
AMOUNT INT,
StartDate DATE,
EFFECTIVESTARTDATE DATE,
EXPIRY_DATE Date,
FREQUENCY INT,
CO_CODE VARCHAR(10),
AsOfDate Date,
SOURCEID_REVISED VARCHAR(255),
PID VARCHAR(5),
DaysInPeriod INT
)
INSERT INTO Reporting_Table(CREDIT_LINE_NO,CURRENCY,AMOUNT,STARTDATE,EFFECTIVESTARTDATE,EXPIRY_DATE,FREQUENCY,CO_CODE,ASOFDATE,SourceID_Revised,PID,DaysInPeriod)
VALUES
('1026321','USD','16875','9/30/2017','9/30/2017','9/30/2019','8','US0010001','7/31/2017','','',''),
('1026321','USD','16875','9/30/2017','12/31/2017','9/30/2019','8','US0010001','7/31/2017','','',''),
('1026321','USD','16875','9/30/2017','3/31/2018','9/30/2019','8','US0010001','7/31/2017','','',''),
('1026321','USD','16875','9/30/2017','6/30/2018','9/30/2019','8','US0010001','7/31/2017','','',''),
('1026321','USD','16875','9/30/2017','9/30/2018','9/30/2019','8','US0010001','7/31/2017','','',''),
('1026321','USD','16875','9/30/2017','12/31/2018','9/30/2019','8','US0010001','7/31/2017','','',''),
('1026321','USD','16875','9/30/2017','3/31/2019','9/30/2019','8','US0010001','7/31/2017','','',''),
('1026321','USD','16875','9/30/2017','6/30/2019','9/30/2019','8','US0010001','7/31/2017','','',''),
('1026329','USD','16875','9/30/2017','9/30/2017','9/30/2019','8','US0010001','7/31/2017','','',''),
('1026329','USD','16875','9/30/2017','12/31/2017','9/30/2019','8','US0010001','7/31/2017','','',''),
('1026329','USD','16875','9/30/2017','3/31/2018','9/30/2019','8','US0010001','7/31/2017','','','')
Select *
From Reporting_Table
Select *
From Reporting_Table
I have this SQL:
with cte as
(
select *, rn = row_number() over (partition by Credit_Line_NO,ASOFDATE order by ASOFDATE)
from Reporting_Table
)
Select *
From cte
Basically, when rn=1, DaysInPeriod = 90, and then it should increment by DateDiff(days,rn-1,rn) for every next rn. It should reset based on the change in Credit_Line_NO & ASOFDATE, so I am using:
partition by Credit_Line_NO,ASOFDATE
Here is a sample of what I want to achieve.
I am using SQL Server 2008, so I can't use the Lead/Lag functions. I put together the SQL below, but it doens't execute.
SELECT T1.CREDIT_LINE_NO,
T1.CURRENCY,
T1.AMOUNT,
T1.STARTDATE,
T1.EFFECTIVESTARTDATE,
T1.EXPIRY_DATE,
T1.FREQUENCY,
T1.CO_CODE,
T1.AsOfDate
MIN(T2.EFFECTIVESTARTDATE) AS Date2,
DATEDIFF("D", T1.EFFECTIVESTARTDATE, MIN(T2.EFFECTIVESTARTDATE)) AS DaysDiff
FROM Reporting_Table T1
LEFT JOIN Reporting_Table T2
ON T1.CREDIT_LINE_NO = T2.CREDIT_LINE_NO
AND T2.EFFECTIVESTARTDATE > T1.EFFECTIVESTARTDATE
GROUP BY T1.CREDIT_LINE_NO,
T1.CURRENCY,
T1.AMOUNT,
T1.STARTDATE,
T1.EFFECTIVESTARTDATE,
T1.EXPIRY_DATE,
T1.FREQUENCY,
T1.CO_CODE,
T1.AsOfDate
Finally, I want to run an UPDATE query, or SELECT * INTO NEW_TABLE query.
Your query fails because line 9 T1.AsOfDate is missing a comma. Joining on AND T2.EFFECTIVESTARTDATE > T1.EFFECTIVESTARTDATE creates a 1 to many join which is not necessary. We can imitate a LAG function by applying row_number in a CTE then joining on T1.rn = T2.rn +1.
Edit: I updated your ROW_NUMBER to order by EFFECTIVESTARTDATE since ASOFDATE is a partition column and will always be the same within a window.
Here is the SQL fiddle for this solution.
You can SELECT INTO this result set into a new table or UPDATE an existing table.
WITH cte AS (
SELECT
Credit_Line_NO,
CURRENCY,
AMOUNT,
StartDate,
EFFECTIVESTARTDATE,
EXPIRY_DATE,
FREQUENCY,
CO_CODE,
AsOfDate,
SOURCEID_REVISED,
PID,
DaysInPeriod,
ROW_NUMBER() OVER (PARTITION BY Credit_Line_NO, ASOFDATE ORDER BY EFFECTIVESTARTDATE) AS rn
FROM Reporting_Table
)
SELECT
T1.Credit_Line_NO,
T1.CURRENCY,
T1.AMOUNT,
T1.StartDate,
T1.EFFECTIVESTARTDATE,
T1.EXPIRY_DATE,
T1.FREQUENCY,
T1.CO_CODE,
T1.AsOfDate,
T1.SOURCEID_REVISED,
T1.PID,
CASE
WHEN T1.rn = 1 THEN 90
ELSE DATEDIFF("D", t2.effectivestartdate, t1.effectivestartdate)
END AS DaysInPreiod,
T1.rn
FROM cte AS t1
LEFT JOIN cte AS t2 ON
t1.credit_line_no = t2.credit_line_no
AND t1.rn = t2.rn + 1
I have this query in sql server database
SELECT [Id]
,[CreatedBy]
,[CreatedDate]
,[ModifiedBy]
,[ModifiedDate]
,[IsDeleted]
,[IsActive]
,[Type]
,[RelaseDate]
,[Prefix]
,[SubTitle]
,[Title]
,[Status]
,[Sequence]
,[Value]
,[Content]
,[Author]
,[Summery]
,[EndDate]
,[ViewedTime]
,[DefaultCategorieId]
,[URLTitle]
,[AlowComments]
,[HideImage]
,[ExternalLink]
FROM [SalesItem].[dbo].[Items]
where Type='7a38bd0c-222f-4308-8dce-f7a2014d7d79' and IsDeleted <> 1
order by [CreatedDate] desc
OFFSET 10 ROWS
FETCH NEXT 20 ROWS ONLY;
the items has about 200,000 record the execution time for this query is about 00:80:30
Is there any way to faster the query. because the same table may have many different queries which they take longer execution time
Considering [Id] as PK with identity enabled and NO index on CreatedDate, You can sort by [ID]
As ordering by both columns will be same. (Hope you are not updating CreatedDate later)
This query should be quicker
;with cte
as
(
SELECT ROW_NUMBER() over (order by [Id]) as rowid,
,[Id]
,[CreatedBy]
,[CreatedDate]
,[ModifiedBy]
,[ModifiedDate]
,[IsDeleted]
,[IsActive]
,[Type]
,[RelaseDate]
,[Prefix]
,[SubTitle]
,[Title]
,[Status]
,[Sequence]
,[Value]
,[Content]
,[Author]
,[Summery]
,[EndDate]
,[ViewedTime]
,[DefaultCategorieId]
,[URLTitle]
,[AlowComments]
,[HideImage]
,[ExternalLink]
FROM [SalesItem].[dbo].[Items]
where Type='7a38bd0c-222f-4308-8dce-f7a2014d7d79' and IsDeleted <> 1
)
select * from cte where rowid between 11 and 20
Please let me know the execution time this takes