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
Related
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 am trying to write a query that returns the time taken by an Order from start to completion.
My table looks like below.
Order No. Action DateTime
111 Start 3/23/2018 8:18
111 Complete 3/23/2018 9:18
112 Start 3/24/2018 6:00
112 Complete 3/24/2018 11:10
Now I am trying to calculate the date difference between start and completion of multiple orders and below is my query:
Declare #StartDate VARCHAR(100), #EndDate VARCHAR(100), #Operation VARCHAR(100)
declare #ORDERTable table
(
order varchar(1000)
)
insert into #ORDERTable values ('111')
insert into #ORDERTable values ('112')
Select #Operation='Boiling'
set #EndDate = (SELECT DATE_TIME from PROCESS WHERE ACTION='COMPLETE' AND ORDER in (select order from #ORDERTable) AND OPERATION=#Operation)
---SELECT #EndDate
set #StartDate = (SELECT DATE_TIME from PROCESS WHERE ACTION='START' AND ORDER in (select order from #ORDERTable) AND OPERATION=#Operation)
---SELECT #StartDate
SELECT DATEDIFF(minute, #StartDate, #EndDate) AS Transaction_Time
So, I am able to input multiple orders but I want to get multiple output as well.
And my second question is if I am able to achieve multiple records as output, how am I gonna make sure which datediff is for which Order?
Awaiting for your answers. Thanks in advance.
I am using MSSQL.
You can aggregate by order number and use MAX or MIN with CASE WHEN to get start or end time:
select
order_no,
max(case when action = 'Start' then date_time end) as start_time,
max(case when action = 'Completed' then date_time end) as end_time,
datediff(
minute,
max(case when action = 'Start' then date_time end),
max(case when action = 'Completed' then date_time end)
) as transaction_time
from process
group by order_no
order by order_no;
You can split up your table into two temp tables, cte's, whatever, and then join them together to find the minutes it took to complete
DECLARE #table1 TABLE (OrderNO INT, Action VARCHAR(100), datetime datetime)
INSERT INTO #table1 (OrderNO, Action, datetime)
VALUES
(111 ,'Start' ,'3/23/2018 8:18'),
(111 ,'Complete' ,'3/23/2018 9:18'),
(112 ,'Start' ,'3/24/2018 6:00'),
(112 ,'Complete' ,'3/24/2018 11:10')
;with cte_start AS (
SELECT orderno, Action, datetime
FROM #table1
WHERE Action = 'Start')
, cte_complete AS (
SELECT orderno, Action, datetime
FROM #table1
WHERE Action = 'Complete')
SELECT
start.OrderNO, DATEDIFF(minute, start.datetime, complete.datetime) AS duration
FROM cte_start start
INNER JOIN cte_complete complete
ON start.OrderNO = complete.OrderNO
Why don't you attempt to approach this problem with a set-based solution? After all, that's what a RDBMS is for. With an assumption that you'd have orders that are of interest to you in a table variable like you described, #ORDERTable(Order), it would go something along the lines of:
SELECT DISTINCT
[Order No.]
, DATEDIFF(
minute,
FIRST_VALUE([DateTime]) OVER (PARTITION BY [Order No.] ORDER BY [DateTime] ASC),
FIRST_VALUE([DateTime]) OVER (PARTITION BY [Order No.] ORDER BY [DateTime] DESC)
) AS Transaction_Time
FROM tableName
WHERE [Order No.] IN (SELECT Order FROM #ORDERTable);
This query works if all the values in the Action attribute are either Start or Complete, but also if there are others in between them.
To read up more on the FIRST_VALUE() window function, check out the documentation.
NOTE: works in SQL Server 2012 or newer versions.
I have the following data
CREATE TABLE [dbo].[Test](
[CustId] [int] NULL,
[Spend] [money] NULL,
[TimeOdSpent] [datetime] NULL,
[ID] [int] IDENTITY(1,1) NOT NULL
) ON [PRIMARY]
GO
SET IDENTITY_INSERT [dbo].[Test] ON
GO
INSERT [dbo].[Test] ([CustId], [Spend], [TimeOdSpent], [ID])
VALUES (11, 400.0000, CAST(N'2016-10-27 10:00:00.000' AS DateTime), 1)
INSERT [dbo].[Test] ([CustId], [Spend], [TimeOdSpent], [ID])
VALUES (11, 200.0000, CAST(N'2016-10-27 11:00:00.000' AS DateTime), 2)
INSERT [dbo].[Test] ([CustId], [Spend], [TimeOdSpent], [ID])
VALUES (11, 400.0000, CAST(N'2016-10-28 09:00:00.000' AS DateTime), 3)
INSERT [dbo].[Test] ([CustId], [Spend], [TimeOdSpent], [ID])
VALUES (11, 500.0000, CAST(N'2016-10-28 16:00:00.000' AS DateTime), 4)
GO
SET IDENTITY_INSERT [dbo].[Test] OFF
Expected Result should be like this
1 2016-10-27 11:00:00.000 600
2 2016-10-28 09:00:00.000 1000
3 2016-10-28 16:00:00.000 900
I want to find out the instances where the spend Totals > 500 within a 24 hour period. Being trying to write a windowing query without luck
You can query as below:
Select * from (
Select *, Sm = sum(spend) over(partition by convert(date,timeofuse)) from #customer
) a
Where Sm > 500
This is the sort of thing I was looking for. I used the Sales.SalesOrderHeader table from AdventureWorks Instead of my simple table above
;WITH cte1 as
(
select
LAG(ShipDate) OVER(PARTITION By SAlesPersonId ORDER BY ShipDate) ShipDateBefore,ShipDate, SalesPersonID,SubTotal,CAST(ShipDate as Date) Date
from Sales.SalesOrderHeader
where CAST(ShipDate as DATE)<'20080710' and SalesPersonID IS NOT NULL
),cte2 as
(Select * ,DATEDIFF(ss,ShipDateBefore,ShipDate) as DiffinDays
from cte1
), cte3 as (
select * ,SUM(DiffinDays) OVER(Partition BY SalesPersonId ORDER BY ShipDate ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as RunningTime
from cte2
),cte4 as
(select
*,ISNULL(CAST((RunningTime / 86400.00) AS INT),0) Cycle
FROM cte3
)
SELECT
SalesPersonID ,SUM(SubTotal)Total,MIN(ShipDate)DurationStart,MAX(ShipDate)DurationStart
from cte4
GROUP by SalesPersonID,Cycle
Having SUM(SubTotal) > 100000.00
The query that I've build does not fit the solution. I've tried many queries but can't get the result I want.
I have the following table
CREATE TABLE [dbo].[test](
[ID] [int] IDENTITY(1,1) NOT NULL,
[ATC] [varchar](15) NOT NULL,
[PID] [varchar](7) NOT NULL,
[NAME] [varchar](50) NOT NULL,
[REG_DATE] [datetime] NULL,
[ACTIVE] [bit] NOT NULL CONSTRAINT [DF_test_ACTIVE] DEFAULT ((1))
) ON [PRIMARY]
Example data
INSERT INTO [dbo].[test] ([ATC],[PID],[NAME],[REG_DATE],[ACTIVE])
VALUES ('A01','123456','TEST1','2016-08-31 00:00:00.000',0);
INSERT INTO [dbo].[test] ([ATC],[PID],[NAME],[REG_DATE],[ACTIVE])
VALUES ('A01','123456','TEST2','2016-09-01 00:00:00.000',0);
INSERT INTO [dbo].[test] ([ATC],[PID],[NAME],[REG_DATE],[ACTIVE])
VALUES ('A01','123456','TEST3','2016-09-02 00:00:00.000',0);
INSERT INTO [dbo].[test] ([ATC],[PID],[NAME],[REG_DATE],[ACTIVE])
VALUES ('A01','123456','TEST4','2016-09-03 00:00:00.000',0);
INSERT INTO [dbo].[test] ([ATC],[PID],[NAME],[REG_DATE],[ACTIVE])
VALUES('A01','123456','TEST5','2016-09-06 00:00:00.000',1);
Example Query:
;WITH CTE AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY ATC, PID ORDER BY REG_DATE ASC) AS ROWNUM,* FROM [dbo].[test]
WHERE ACTIVE=0
)
SELECT DATEDIFF(DAY, d2.REG_DATE, d1.REG_DATE),d1.NAME, d1.REG_DATE AS ACTIVE_REC_DATE, d2.REG_DATE AS NOTACTIVE_REC_DATE, d1.ACTIVE, d2.ACTIVE FROM [dbo].[test] as d1
LEFT JOIN CTE d2 ON d2.ATC = d1.ATC AND d2.PID = d1.PID
AND DATEDIFF(DAY, d2.REG_DATE, d1.REG_DATE) <= 7
WHERE d1.ACTIVE=1 AND d1.PID=123456;
Wanted result:
The record with column ACTIVE True (1) should contain (if the record exists) the REG_DATE of the not active previous record max 7 days old. Like:
(No column name) NAME ACTIVE_REC_DATE NOTACTIVE_REC_DATE ACTIVE ACTIVE
3 TEST5 2016-09-06 00:00:00.000 2016-08-31 00:00:00.000 1 0
Currently the query result contains multiple records because the are more records that will fall in the 7 days period. I need to join 1 record that will be the max 7 day old one.
I've used the ROW_NUMBER() with over partition by so I can use and identify the first record because I will be sorting ascending. This doesn't work when there is no previous records available or the previous records are older then 7 days
When there is no records to join is ignored can use INNER JOIN or date columns at null
I hope I'm clear with my explanation.
SELECT DATEDIFF(DAY, d2.REG_DATE, d1.REG_DATE),d1.NAME, d1.REG_DATE AS ACTIVE_REC_DATE, d2.REG_DATE AS NOTACTIVE_REC_DATE, d1.ACTIVE, d2.ACTIVE FROM [dbo].[test] as d1
OUTER APPLY
(SELECT TOP 1 T1.* FROM dbo.test t1 WHERE t1.ATC = d1.ATC AND t1.PID = d1.PID and DATEDIFF(DAY, t1.REG_DATE, d1.REG_DATE) <= 7 order by t1.REG_DATE desc) d2
WHERE d1.ACTIVE=1 AND d1.PID=123456;
You can return a single row for a PID by adding a filter on ROWNUM to your left join:
;WITH CTE AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY ATC, PID ORDER BY REG_DATE ASC) AS ROWNUM,* FROM [dbo].[test]
WHERE ACTIVE=0
)
SELECT DATEDIFF(DAY, d2.REG_DATE, d1.REG_DATE),d1.NAME, d1.REG_DATE AS ACTIVE_REC_DATE, d2.REG_DATE AS NOTACTIVE_REC_DATE, d1.ACTIVE, d2.ACTIVE FROM [dbo].[test] as d1
LEFT JOIN CTE d2 ON d2.ATC = d1.ATC AND d2.PID = d1.PID
AND DATEDIFF(DAY, d2.REG_DATE, d1.REG_DATE) <= 7
AND d2.ROWNUM = 1
WHERE d1.ACTIVE=1 AND d1.PID=123456;
It's not clear from the question how the query should behave when no previous records from the last seven days exist.
sQL FIDDLE
CREATE TABLE [STUDENT_MASTER]
(
[User_ID] [int] IDENTITY (1, 1) NOT NULL CONSTRAINT STUDENT_MASTER_P_KEY PRIMARY KEY,
[Name] [varchar] (50),
[START_DATE] [varchar] (50),
[PRIORITY] [varchar] (50)
)
INSERT INTO STUDENT_MASTER
VALUES('JOHN','2013-08-16','4')
INSERT INTO STUDENT_MASTER
VALUES('JACK','2013-08-10','')
INSERT INTO STUDENT_MASTER
VALUES('MACK','','1')
INSERT INTO STUDENT_MASTER
VALUES('ACK','2013-08-15','2')
//SQL QUERY
SELECT ROW_NUMBER() OVER
(ORDER BY CASE
WHEN STUDENT_MASTER.START_DATE IS NULL THEN 1
WHEN STUDENT_MASTER.PRIORITY IS NULL THEN 1
ELSE 0 END,STUDENT_MASTER.START_DATE DESC ,STUDENT_MASTER.PRIORITY DESC
)AS RowNumber,STUDENT_MASTER.START_DATE
FROM STUDENT_MASTER
HOW TO QUERY ORDER BY DESC START DATE AND NULL VALUE BE FIRST
DEMO
ORDER BY
CASE WHEN START_DATE = '' THEN 0 ELSE 1 END ASC,
START_DATE DESC
You could use ISNULL, but your column does not have null values - it has empty strings intead.
And another problem - because your START_DATE column is VARCHAR, not DATETIME it will perform string, alphabetical sort instead of datetime sorting.
Don't simply order by StartDate, order by StartDate or a default value if it's null.
ORDER BY ISNULL(StartDate, '9999-12-31') DESC
Oh... and have your date be an actual datetime. Otherwise you'll just be doing an alphabetical sort.
Considering that is for purpose your date as string, this would be my solution for the problem
SELECT
start_date
FROM
STUDENT_MASTER
ORDER BY
LEN(start_date),
start_date DESC
Follows the SQL Fiddle