I have a data looks like this from my database. My report will have 2 parameters, Start Date and End Date.
For example if the user select Start Date = 1/1/2015 and End Date = 12/31/2015, the report should return user TierCode = Privilege.
I've achieve this using Stored Procedure. But the performance is an issues here as I couldn't use Extract Data.
SELECT
m.MemberID,
ty.TierCode AS TierCodeTY,
ly.TierCode AS TierCodeLY
FROM
Tangs_dim_Member m LEFT JOIN
(
SELECT TierCode, MemberID, ROW_NUMBER() OVER(PARTITION BY MemberID ORDER BY SnapshotDate DESC) AS i
FROM dw_Tangs_MonthlySnapshot
WHERE SnapshotDate >= #StartDate AND SnapshotDate <= #EndDate
) ty
ON
ty.MemberID = m.MemberID AND ty.i = 1 LEFT JOIN
(
SELECT TierCode, MemberID, ROW_NUMBER() OVER(PARTITION BY MemberID ORDER BY SnapshotDate DESC) AS i
FROM dw_Tangs_MonthlySnapshot
WHERE SnapshotDate >= DATEADD(year,-1,#StartDate) AND SnapshotDate <= DATEADD(year,-1,#EndDate)
) ly
ON
ly.MemberID = m.MemberID AND ly.i = 1
How can I achieve above report without using Stored Procedure. Calculated field is fine as long I can use Views / Custom Query / Tables + Extract Data. So the data will be filtered at Tableau instead at the database.
I think you can get the last inserted record by adding Ordered by [date]
and get just first one by adding [Limit 1] in the sql query
hope that help you
Related
I have created a stored procedure to get data. In this stored procedure, I have returned 1 table and table stores the data above 1 lakh + data. So right now I have run the stored procedure that time I get the data in above 1 minute time take. I want just with in 1 second get data. I have set also SET NOCOUNT ON; and also create missing index. Still I am getting same time for the get data.
This is my query:
DECLARE #CurMon int
DECLARE #year nvarchar(max)
SELECT #CurMon = month(getdate())
SELECT #year = year(getdate())
SELECT
FORMAT(dateadd(MM, T.i, getdate()),'MMM-yy') AS DateColumn,
ISNULL(uf.TotalCount, 0) as TotalCount
FROM
(VALUES (12-#CurMon),(11-#CurMon),(10-#CurMon),(9-#CurMon),(8-#CurMon),(7-#CurMon),(6-#CurMon), (5-#CurMon), (4-#CurMon), (3-#CurMon), (2-#CurMon), (1-#CurMon)) AS T(i)
OUTER APPLY
(SELECT DISTINCT
COUNT(datepart(MM,UF.InsertDateTime)) OVER (partition by datepart(MM,UF.InsertDateTime)) AS TotalCount
FROM dbo.UserFollowers UF
INNER JOIN dbo.Users U on U.UserId = UF.FollowerId
WHERE DATEDIFF(mm,UF.InsertDateTime, DATEADD(mm, T.i, GETDATE())) = 0 and UF.IsFollowed = 1
) uf
order by DATEPART(MM,convert(datetime,FORMAT(dateadd(MM, T.i, getdate()),'MMMM') +'01 '+#year,110))
i am also try some other query for the improve speed of query but still i am getting same time. here this query also print.
declare #StartDate datetime = dateadd(year , datediff(year , 0, getdate() ) , 0)
declare #tempT2 table
(
MNo int,
[Month] datetime,
NextMonth datetime)
;with Months as (
select top (12)
MNo = row_number() over (order by number)
,[Month] = dateadd(month, row_number() over (order by number) -1, #StartDate)
, NextMonth = dateadd(month, row_number() over (order by number), #StartDate)
from master.dbo.spt_values
)
insert into #tempT2
select * from Months
select
m.MNo
, Month = format(m.Month, 'MMM-yy')
, tally = count(UF.InsertDateTime)
from #tempT2 m
left join dbo.UserFollowers UF
INNER JOIN dbo.Users U on U.UserId = UF.FollowerId
on UF.InsertDateTime >= m.Month
and UF.InsertDateTime < m.NextMonth where UF.IsFollowed = 1
group by m.MNo,format(m.Month, 'MMM-yy')
order by MNo
here this is my both query i have try but still i am not getting success for the improve the speed of the query. and sorry but i can not see here my execution plan of the query actually i have not permission for that.
You can gain a little bit of performance by switching to a temporary table instead of a table variable, and by getting rid of format():
declare #StartDate datetime = dateadd(year , datediff(year , 0, getdate() ) , 0)
create table #Months (
MNo int not null primary key
, Month char(6) not null
, MonthStart datetime not null
, NextMonth datetime not null
)
;with Months as (
select top (12)
MNo = row_number() over (order by number)
, MonthStart = dateadd(month, row_number() over (order by number) -1, #StartDate)
, NextMonth = dateadd(month, row_number() over (order by number), #StartDate)
from master.dbo.spt_values
)
insert into #Months (MNo, Month, MonthStart, NextMonth)
select
MNo
, Month = stuff(convert(varchar(9),MonthStart,6),1,3,'')
, MonthStart
, NextMonth
from Months;
select
m.MNo
, m.Month
, tally = count(UF.InsertDateTime)
from #tempT2 m
inner join dbo.Users U
on UF.InsertDateTime >= m.MonthStart
and UF.InsertDateTime < m.NextMonth
inner join dbo.UserFollowers UF
on U.UserId = UF.FollowerId
and UF.IsFollowed = 1
group by
m.MNo
, m.Month
order by MNo
After that, you should evaluate the execution plan to determine if you need a better indexing strategy.
If you still need it to go faster, you could create an actual calendar table and look into creating an indexed view. An indexed view can be a chore get it to behave correctly depending on your sql server version, but will be faster.
Reference:
format performance - Aaron Bertrand
What is the difference between a temp table and table variable in SQL Server? - Answer by Martin Smith
When should I use a table variable vs temporary table in sql server? - Answer by Martin Smith
Indexed Views and Statistics - Paul White
Generate a set or sequence without loops - 2 - Aaron Bertrand
The "Numbers" or "Tally" Table: What it is and how it replaces a loop - Jeff Moden
Creating a Date Table/Dimension in sql Server 2008 - David Stein
Calendar Tables - Why You Need One - David Stein
Creating a date dimension or calendar table in sql Server - Aaron Bertrand
i trying to assign the month wise leaves in year in my database(sql server) but its fail below my trying code is there
select count(*)(SELECT DATEPART(yyyy,Date) AS OrderYear,
DATEPART(mm,Date) AS OrderMonth,
DATEPART(dd,Date) AS OrderDay
FROM Leaves
WHERE EmployeeID=37) FROM Leaves where OrderYear = '2016'
This below code will do what you need to do. Just had to make some small changes to your above code:
you will need an inner query which will extract the date parts, which will then need to be selected.
An outer query will need to then select from the inner query and filter on the new date part field.
Here is how I would do it:
select
count(*)
FROM
(
SELECT
DATEPART(yyyy, Date) AS OrderYear,
DATEPART(mm, Date) AS OrderMonth,
DATEPART(dd, Date) AS OrderDay
FROM
Leaves
WHERE
EmployeeID = 37
) A
WHERE
OrderYear = '2016';
You may looking for this :
;WITH CTE AS(
SELECT 1 as MonthNumber
UNION ALL
SELECT MonthNumber+1 AS MonthNumber
FROM CTE
WHERE MonthNumber<=12
)
SELECT MonthNumber,COUNT(*)
FROM CTE
LEFT JOIN Leaves ON MONTH(DATE) = MonthNumber AND EmployeeID=37 AND YEAR(Date)=2016
GROUP BY MonthNumber
This is a question using SQL Server. Is there a more elegant way of doing this?
Considering a table mytable (I'm using Unix timestamps, I've converted these to readable dates for ease of reading)
ID Foreign_ID Date
-------------------------
1 1 01-Jul-15
2 2 01-Sep-16
3 3 05-Aug-16
4 2 01-Sep-15
I would like to extract the Foreign_ID where the most recent record's (highest ID) date is in a range, which is this example is the 1st January 2016 to 31st December 2016. The following works if substituting the dates for timestamps:
select distinct
Foreign_ID
from
mytable l1
where
(select top 1 Date
from mytable l2
where l2.Foreign_ID = l1.Foreign_ID
order by ID desc) >= **1 Jan 2016**
and
(select top 1 Date
from mytable l2
where l2.Foreign_ID = l1.Foreign_ID
order by ID desc) <= **31 Dec 2016**
That should only return Foreign_ID = 3. A simpler query would also return Foreign_ID 2 which would be wrong, as it has a more recent record dated out of the above ranges
This will all form part of a bigger query
Assuming SQL Server 2005+, you can use ROW_NUMBER:
WITH CTE AS
(
SELECT *,
RN = ROW_NUMBER() OVER( PARTITION BY Foreign_ID
ORDER BY ID DESC)
FROM dbo.YourTable
WHERE [Date] >= '01-Jan-2016' -- you need to use the right
AND [Date] <= '31-Dec-2016' -- date format here
)
SELECT Foreign_ID
FROM CTE
WHERE RN = 1;
If it's SQL Server 2008+, you can use this:
select foreign_id
from (
select foreign_id, row_number() over (order by id desc) as rseq
from myTable
where Date >= value1 and Date <= value2
) as x
where rseq = 1
Just fill in the date values, and you might have to put brackets or quotes around the column named "Date", since it is also a keyword.
I am using the SUM Over the first time and have the following query noe:
SELECT Id, Amount, Date, TotalAmount = SUM(Amount) OVER (order by Amount)
FROM Account
WHERE Date >= '2016-03-01' AND Date <= '2016-03-10' AND UserId = 'xyz'
ORDER BY ValutaDate
The TotalAmount should be a running total over the whole table for a specific user (so the Sum Over clause should respect the where clause for the user). On the other hand I just need a few records and not the whole table, thats why I added the where clause specifying the date range. But now, of course, my sum gets calculated just for the range I specified.
What should I do, to get just a few records specified by date range but get the sum calculated over the whole table though. Is there an performant way to accomplish this?
Thanks in advance for helping me out.
Break the running total into its own query
; WITH all_rows_one_user as (SELECT *
, TotalAmount = SUM(Amount) OVER (order by ValutaDate)
FROM Account
WHERE UserId = 'xyz')
SELECT Id, Amount, Date, TotalAmount
FROM all_rows_one_user
WHERE Date >= '2016-03-01' AND Date <= '2016-03-10'
ORDER BY ValutaDate
Same query, different syntax:
SELECT Id, Amount, Date, TotalAmount
FROM (SELECT *
, TotalAmount = SUM(Amount) OVER (order by ValutaDate)
FROM Account
WHERE UserId = 'xyz') AS all_rows_one_user
WHERE Date >= '2016-03-01' AND Date <= '2016-03-10'
ORDER BY ValutaDate
The WHERE clause is applied first so the SUM can't access rows that don't match that.
You can use apply though. Note it will be reading the entire table for that user so might not perform too well without a decent index.
SELECT a.Id, a.Amount, a.Date, ta.TotalAmount
FROM Account a
OUTER APPLY (SELECT SUM(CASE WHEN a2.Date <= Account.Date THEN a2.TotalAmount ELSE 0 END) AS TotalAmount FROM Account a2 WHERE a2.UserId = Account.UserId) ta
WHERE a.Date >= '2016-03-01' AND a.Date <= '2016-03-10' AND a.UserId = 'xyz'
Is there any way to use ROW_NUMBER() in SQL without using OVER, because I want to use sorting.
I have a Grid with multiple sortable columns with configurable rows. In my scenario order by is variable that's why I am not able to put order by using ROWNUM.
select ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) as number from Task order by RuleId
I approached this using a different logic. Instead of checking for every single day and verifying it is a weekday or a holiday I create a table which lists only the business days and then take the nth value.
CREATE OR REPLACE FUNCTION add_n_working_days (
start_date DATE, working_days PLS_INTEGER
) RETURN DATE AS
l_end_date DATE := start_date;
l_counter pls_integer := 0;
BEGIN
SELECT
business_day
INTO l_end_date
FROM
(
WITH
dates AS
(SELECT start_date + level - 1 as dt FROM dual CONNECT BY level < 100)
,weekdates AS
(SELECT dt as weekday FROM dates WHERE TO_CHAR(dt,'fmdy') NOT IN ('sat','sun'))
,business_days AS
(
SELECT weekday as business_day FROM weekdates
MINUS
SELECT holiday FROM so_holidays
)
SELECT business_day, ROW_NUMBER() OVER (ORDER BY 1) as rn from business_days
)
WHERE rn = working_days + 1;
RETURN l_end_date;
END add_n_working_days;
If working_days gets too high then I suggest making that value 100 a variable.
select
a.City,
a.len
from
(
select city,length(city) AS len,
row_number()over(order by length(city) desc, city) as hi,
row_number()over(order by length(city), city) as lo
from
STATION) as a where (a.lo=1 or a.hi=1);