I have the following query to optimize.
Following used to get the details from the profit table.
First inner SELECT: in the first select statement I have to get the details from the profit table and assign the row number for each row.
Second inner SELECT: in the second select statement I have to do some calculation (sum).
Outer SELECT: And get the result by combining them on id and also do some manipulation with data.
Code:
SELECT
a.p_id, p_Name,
convert(varchar, a.EndDate, 107) EndDate,
convert(varchar, a.EndDate, 106) NewEndDate,
LTRIM(a.p_id)) + '' + REPLACE(LEFT(CONVERT(VARCHAR, a.EndDate, 106), 6) + '' + RIGHT(CONVERT(VARCHAR, a.EndDate, 106), 2), ' ', '') as Compo,
a.GP,
b.fpro FirstProfit,
(b.fpro - b.spro) prodiff,
a.Qtity * a.GP as Ov,
a.Qtity,
b.fproChanPer
FROM
(SELECT
p_Name, p_id,
EndDate,
GP, FirstProfit,
prodiff,
Qtity,
ROW_NUMBER() OVER (PARTITION By p_Name, p_id ORDER BY EndDate) Rown
FROM
tbl_profit) a,
(SELECT
p_id,
CAST(SUM(FirstProfit) AS DECIMAL(24,2)) fpro,
CAST(SUM(SecondProfit) AS DECIMAL(24,2)) spro,
CAST(CAST(SUM(prodiff) AS DECIMAL(24,2)) / CAST(SUM(SecondProfit) AS DECIMAL(24,2)) * 100 AS DECIMAL(24,2)) fproChanPer
FROM
tbl_profit
GROUP BY
p_id) b
WHERE
b.p_id = a.p_id
AND Rown = 1
My question: can I combine those two (alias a and b) inner SELECT statements for query optimization?
My attempt: I have tried by using the following query but getting different calculation result.
SELECT p_Name,
p_id,
EndDate,
GP,
FirstProfit,
prodiff,
Qtity
ROW_NUMBER()OVER (PARTITION By p_Name,p_id ORDER By EndDate ) Rown,
CAST(SUM(FirstProfit) AS DECIMAL(24,2)) fpro,
CAST(SUM(SecondProfit) AS DECIMAL(24,2)) spro,
CAST(CAST(SUM(prodiff) AS DECIMAL(24,2)) /CAST(SUM(SecondProfit) AS DECIMAL(24,2)) * 100 AS DECIMAL(24,2)) fproChanPer
FROM tbl_profit
GROUP By p_id,p_Name,EndDate,GP,FirstProfit,prodiff,Qtity
tbl_profit must have multiple P_Name for a single p_id , when you are grouping by p_id only you are getting correct aggregate value whereas because of multiple p_name for a single p_id when you are grouping by p_id and p_name your aggregate sum values are getting double for wherever single p_id has more than one p_name.
The way you written query to get Rown = 1 records in this case is perfect, thought still have scope to optimize I have done few things please check.
SELECT
a.p_id,
p_Name,
CONVERT(varchar, a.EndDate, 107) EndDate,
CONVERT(varchar, a.EndDate, 106) NewEndDate,
LTRIM(a.p_id) + '' + REPLACE(LEFT(CONVERT(varchar, a.EndDate, 106), 6) + '' + RIGHT(CONVERT(varchar, a.EndDate, 106), 2), ' ', '') AS Compo,
a.GP,
b.fpro AS FirstProfit,
(b.fpro - b.spro) prodiff,
a.Qtity * a.GP AS Ov,
a.Qtity,
b.fproChanPer
FROM (
SELECT
a.p_Name,
a.p_id,
a.EndDate,
a.GP,
a.FirstProfit,
a.prodiff,
a.Qtity,
ROW_NUMBER() OVER (PARTITION BY a.p_Name, a.p_id ORDER BY a.EndDate) Rown
FROM tbl_profit AS a WITH (NOLOCK)
) AS a
INNER JOIN
(
SELECT
b.p_id,
CAST(SUM(b.FirstProfit) AS decimal(24, 2)) fpro,
CAST(SUM(b.SecondProfit) AS decimal(24, 2)) spro,
CAST(CAST(SUM(b.prodiff) AS decimal(24, 2)) / CAST(SUM(b.SecondProfit) AS decimal(24, 2)) * 100 AS decimal(24, 2)) fproChanPer
FROM tbl_profit AS b WITH (NOLOCK)
GROUP BY b.p_id
) AS b
ON b.p_id = a.p_id
AND a.Rown = 1
Related
I have a data that looks as below. I would like to count total number of products and create a table that summarize the count for different date range. Please see diagram below as an example. Here the quay I have:
SELECT(
SELECT Count(DISTINCT Product) FROM Table1 WHERE Date BETWEEN '01/01/2017 AND
'01/15/2017,
SELECT Count(DISTINCT Product) FROM Table1 WHERE Date BETWEEN '01/16/2017 AND
'01/31/2017,
);
But I get an error:
Incorrect syntax near ','
This will work
SELECT
(SELECT Count(DISTINCT Product) FROM Table1 WHERE Date BETWEEN '01/01/2017' AND
'01/15/2017'),
(SELECT Count(DISTINCT Product) FROM Table1 WHERE Date BETWEEN '01/16/2017' AND
'01/31/2017')
;
You have a few syntax errors in your SQL:
when using the BETWEEN statement, you must only put quotes around
each date. Don't include the AND in the quotes.
You need to put brackets around each of the inner SELECTS.
There is an extra comma at the end (before the closing bracket)
However, this query will not return values of 5 and 2, because you are specifying DISTINCT in each SELECT. That will give you only 3 and 2 because there are only 3 distinct values for Product (A/D/E) returned from the first query. Remove the distinct if you want the number of rows.
Finally, I recommend that you use the YYYY-MM-DD syntax when using date literals in your SQL. This removes any ambiguity about what is the date and the month. For example, 1/4/2017 could be 4 Jan or 1 April, depending on how your SQL Server was configured. If you specify 2017-04-01, then this will always be interpreted as 1 April, and 2017-01-04 will always be 4 Jan.
Simply :
SELECT
(SELECT COUNT(DISTINCT Product) FROM yourtable T1 WHERE Date BETWEEN '01/01/2017' AND '01/15/2017') C1,
(SELECT COUNT(DISTINCT Product) FROM yourtable T2 WHERE Date BETWEEN '01/16/2017' AND '01/31/201') C2
Outputs: 3 | 2
SELECT
(SELECT COUNT(*) FROM yourtable T1 WHERE Date BETWEEN '01/01/2017' AND '01/15/2017') C1,
(SELECT COUNT(*) FROM yourtable T2 WHERE Date BETWEEN '01/16/2017' AND '01/31/201') C2
Outputs : 5 | 2
If the last code doesn't work you need to use Convert and Cast:
Declare #tb table(product varchar(50),[Date] date)
insert into #tb
select 'A' as Product, cast(substring(CONVERT(VARCHAR(10), cast('1/1/2017' as date),110),7,4) + '-' +
substring(CONVERT(VARCHAR(10), cast('1/1/2017' as date),110),1,2) + '-' +
substring(CONVERT(VARCHAR(10), cast('1/1/2017' as date),110),4,2) as date) as [Date] union all
select 'A' as Product, cast(substring(CONVERT(VARCHAR(10), cast('1/1/2017' as date),110),7,4) + '-' +
substring(CONVERT(VARCHAR(10), cast('1/1/2017' as date),110),1,2) + '-' +
substring(CONVERT(VARCHAR(10), cast('1/1/2017' as date),110),4,2)as date) as [Date] union all
select 'D' as Product, cast(substring(CONVERT(VARCHAR(10), cast('1/5/2017' as date),110),7,4) + '-' +
substring(CONVERT(VARCHAR(10), cast('1/5/2017' as date),110),1,2) + '-' +
substring(CONVERT(VARCHAR(10), cast('1/5/2017' as date),110),4,2) as date) as [Date] union all
select 'E' as Product, cast(substring(CONVERT(VARCHAR(10), cast('1/6/2017' as date),110),7,4) + '-' +
substring(CONVERT(VARCHAR(10), cast('1/6/2017' as date),110),1,2) + '-' +
substring(CONVERT(VARCHAR(10), cast('1/6/2017' as date),110),4,2)as date) as [Date] union all
select 'E' as Product, cast(substring(CONVERT(VARCHAR(10), cast('1/10/2017' as date),110),7,4) + '-' +
substring(CONVERT(VARCHAR(10), cast('1/10/2017' as date),110),1,2) + '-' +
substring(CONVERT(VARCHAR(10), cast('1/10/2017' as date),110),4,2)as date) as [Date] union all
select 'D' as Product, cast(substring(CONVERT(VARCHAR(10), cast('1/25/2017' as date),110),7,4) + '-' +
substring(CONVERT(VARCHAR(10), cast('1/25/2017' as date),110),1,2) + '-' +
substring(CONVERT(VARCHAR(10), cast('1/25/2017' as date),110),4,2)as date) as [Date] union all
select 'A' as Product, cast(substring(CONVERT(VARCHAR(10), cast('1/30/2017' as date),110),7,4) + '-' +
substring(CONVERT(VARCHAR(10), cast('1/30/2017' as date),110),1,2) + '-' +
substring(CONVERT(VARCHAR(10), cast('1/30/2017' as date),110),4,2)as date) as [Date]
--Copy from here
select acnt as 'Count(01/01/2017-01/15/2017)',bcnt as 'Count(01/16/2017-01/31/2017)' from
(select count(1) as acnt from #tb where [Date] BETWEEN '01/01/2017' AND '01/15/2017') as a
left join
(select count(1) as bcnt from #tb where [Date] BETWEEN '01/16/2017' AND '01/31/2017') as b
On a.acnt != b.bcnt or a.acnt = b.bcnt
;WITH CTE(Product ,[Date])
As
(
SELECT 'A','1/1/2017' Union all
SELECT 'A','1/1/2017' Union all
SELECT 'D','1/5/2017' Union all
SELECT 'E','1/6/2017' Union all
SELECT 'E','1/10/2017' Union all
SELECT 'D','1/25/2017' Union all
SELECT 'A','1/30/2017'
)
SELECT SUM([Count Of between '1/1/2017' AND '1/15/2017']) AS [Count Of between (1/1/2017 AND 1/15/2017)]
,SUM([Count Of between '1/16/2017' AND '1/31/2017']) AS [Count Of between (1/16/2017 AND 1/31/2017)]
FROM (
SELECT COUNT(Product) OVER (
PARTITION BY [Date] ORDER BY Product
) AS [Count Of between '1/1/2017' AND '1/15/2017']
,ISNULL(NULL, '') AS [Count Of between '1/16/2017' AND '1/31/2017']
FROM cte
WHERE [Date] BETWEEN '1/1/2017'
AND '1/15/2017'
UNION ALL
SELECT ISNULL(NULL, '')
,COUNT(Product) OVER (
PARTITION BY [Date] ORDER BY Product
) AS [Count Of between '1/16/2017' AND '1/31/2017']
FROM cte
WHERE [Date] BETWEEN '1/16/2017'
AND '1/31/2017'
) Dt
OutPut
Count Of between (1/1/2017 AND 1/15/2017) | Count Of between (1/16/2017 AND 1/31/2017)
--------------------------------------------------------------------------------
5 2
How do I show the missing days in the query below which uses a join on a calendar table in SQL Server 2012?
The values for the missing day should be 0.
;WITH g AS
(
SELECT calendar.year, calendar.month, calendar.day, calendar.MonthName,
CAST(calendar.day AS NVARCHAR(2)) + '.' + CAST(calendar.month AS NVARCHAR(2)) + '.' + CAST(calendar.year AS NVARCHAR(16))AS DayMonthAndYear,
calendar.MonthName + ' ' + CAST(calendar.year AS NVARCHAR(16)) AS MonthNameAndYear,
COUNT(profileviews.ID) AS total_profileviews
FROM core_Calendar AS calendar
LEFT JOIN members_ProfileViews AS profileviews
ON CONVERT(DATE, calendar.date) = CONVERT(DATE, profileviews.CreatedAt)
WHERE calendar.date >= CONVERT(DATE, '03.02.2015')
AND calendar.date <= CONVERT(DATE, '03.02.2016')
AND profileviews.MemberID = 10
GROUP BY calendar.year, calendar.month, calendar.day, calendar.MonthName
)
SELECT g.year, g.month, g.day, g.MonthName, g.DayMonthAndYear, g.MonthNameAndYear, total_profileviews,
SUM(g.total_profileviews) OVER (ORDER BY g.year,g.month,g.day ROWS UNBOUNDED PRECEDING) AS rt_profileviews
FROM g
ORDER BY g.year, g.month, g.day;
You need to move the profileviews.MemberID = 10 to the LEFT JOIN, since it's essentially converting it to an INNER JOIN:
WITH g AS
(
SELECT C.[year],
C.[month],
C.[day],
C.[MonthName],
CONVERT(VARCHAR(10),C.[date],104) AS DayMonthAndYear,
C.[MonthName] + ' ' + CAST(C.[year] AS NVARCHAR(16)) AS MonthNameAndYear,
COUNT(profileviews.ID) AS total_profileviews
FROM core_Calendar AS C
LEFT JOIN ( SELECT *
FROM members_ProfileViews
WHERE MemberID = 10) AS P
ON CONVERT(DATE, C.[date]) = CONVERT(DATE, P.CreatedAt)
WHERE C.[date] >= CONVERT(DATE, '03.02.2015',104)
AND C.[date] <= CONVERT(DATE, '03.02.2016',104)
GROUP BY C.[year], C.[month], C.[day], C.[MonthName]
)
SELECT g.[year],
g.[month],
g.[day],
g.[MonthName],
g.DayMonthAndYear,
g.MonthNameAndYear,
total_profileviews,
SUM(g.total_profileviews) OVER (ORDER BY g.[year],g.[month],g.[day] ROWS UNBOUNDED PRECEDING) AS rt_profileviews
FROM g
ORDER BY g.[year], g.[month], g.[day];
I have a table checks2:
AllTaskNo int,
CheckQuosimaNo int,
Masroof numeric(18,3),
Maqbood numeric(18,3),
Date1 smalldatetime
I have a query to get balance:
SELECT
AllTaskNo
,CheckQuosimaNo
,Masroof
,Maqbood
,Date
,(SELECT 0 + SUM(Maqbood - Masroof) AS Expr1
FROM Checks2 AS t2
WHERE (BankNo = 6)
AND (CheckQuosimaNo <= Checks2.CheckQuosimaNo)
AND (Date1 BETWEEN CONVERT(DATETIME, '01/01/2014', 103)
AND CONVERT(DATETIME, '31/10/2015', 103)) AND AllTaskNo
IN (SELECT No
FROM AllTasks
WHERE (BankNo = 6)
AND (Date BETWEEN CONVERT(DATETIME, '01/01/2014', 103)
AND CONVERT(DATETIME, '31/10/2015', 103))))) AS NetAmount
FROM
Checks2
WHERE
(BankNo = 6)
AND
(Date1 BETWEEN CONVERT(DATETIME, '01/01/2014', 103)
AND CONVERT(DATETIME, '31/10/2015', 103))
ORDER BY
BankNo
,Date1
,CheckQuosimaNo
the result is:
enter image description here
I need to make a formula (like excel for example NetAmount = NetAmount before + Maqbood - Masroof)
I expect that the selected row in NetAmount = 656360 but then query produce NetAmount=5567675.976
How can I fix this problem
There are many way to do this like Over and Correlated Sub Queries
Over:
SELECT
AllTaskNo ,Maqbood , Masroof,
SUM(-1*Maqbood +Masroof) OVER (ORDER BY AllTaskNo ) AS NetAmount
FROM Checks2 T1
Correlated Sub Queries (Need Sql server 2014+)
SELECT
AllTaskNo ,Maqbood , Masroof,
(
SELECT
SUM(-1*Maqbood +Masroof)
FROM Checks2 T2
WHERE T2.AllTaskNo <=T1.AllTaskNo
) AS NetAmount FROM Checks2 T1
Feel free to comment if you need any further assistance on this item
I have been solve my problem by my own solution ... I think there is a shorter solution .. but any way I get what I want... My solution is:
1- Make a table (Checks3) with a new field named (rank) which is the correct order of the command result.
2- Create a new command mostly like the first one containing the (bank balance formula).
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[Checks3]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table [dbo].[Checks3]
SELECT Checks2.BankNo, Checks2.AllTaskNo, Checks2.CheckQuosimaNo, Checks2.Masroof, Checks2.Maqbood, Checks2.Date1, rank() OVER (ORDER BY checks2.date1, checks2.CheckQuosimaNo) as rank
INTO Checks3
FROM Checks2 INNER JOIN
AllTasks ON Checks2.AllTaskNo = AllTasks.No where Checks2.BankNo =6 and (Checks2.[Date1] BETWEEN CONVERT(DATETIME, '01/10/2015', 103) AND CONVERT(DATETIME,'31/10/2015', 103)) ORDER BY CHECKS2.BankNo, CHECKS2.CheckQuosimaNo
SELECT Checks3.AllTaskNo, Checks3.CheckQuosimaNo, Checks3.Masroof, Checks3.Maqbood, Checks3.Date1,
( SELECT 0+SUM(t2.Maqbood - t2.Masroof) FROM Checks3 t2
WHERE t2.BankNo =6 and t2.rank <= Checks3.rank
and (t2.[Date1] BETWEEN CONVERT(DATETIME, '01/10/2014', 103) AND CONVERT(DATETIME,'31/10/2015', 103))
) as NetAmount
FROM Checks3
where Checks3.BankNo =6 and (Checks3.[Date1] BETWEEN CONVERT(DATETIME, '01/10/2014', 103) AND CONVERT(DATETIME,'31/10/2015', 103)) ORDER BY CHECKS3.RANK
I'm trying to get the SUM of the quantity sold on a day. I need the results to be in ORDER BY date. The query below gives me the exact result I need, except the date is not formatted to what I need.
SELECT CAST(Datetime AS DATE) AS 'date', SUM(quantity) as total_quantity
FROM Invoice_Itemized ii
INNER JOIN Invoice_Totals it ON it.Invoice_Number = ii.Invoice_Number
WHERE ii.ItemNum = '4011'
AND it.datetime > '05/15/2015'
GROUP BY CAST(Datetime AS DATE)
SELECT DATENAME(MM, datetime) + ' ' + CAST(DAY(datetime) AS VARCHAR(2)) AS [DD Month], SUM(quantity) as total_quantity
FROM Invoice_Itemized ii
INNER JOIN Invoice_Totals it ON it.Invoice_Number = ii.Invoice_Number
WHERE ii.ItemNum = '4011'
AND it.datetime > '05/15/2015'
GROUP BY DATENAME(MM, datetime) + ' ' + CAST(DAY(datetime) AS VARCHAR(2))
Results for the top query:
2015-05-15 91.43
2015-05-16 84.77
Results for the bottom query:
June 1 128.34
June 10 85.06
The top query gives me the information I need in the order I need it by.
The bottom query gives me the date format I need but in the wrong order.
If you really need to apply different formatting, you can do it using a derived table:
select
DATENAME(MM, [date]) + ' ' + CAST(DAY([date]) AS VARCHAR(2)) AS [DD Month],
total_quantity
from
(
SELECT CAST([Datetime] AS DATE) AS [date], SUM(quantity) as total_quantity
FROM Invoice_Itemized ii
INNER JOIN Invoice_Totals it ON it.Invoice_Number = ii.Invoice_Number
WHERE ii.ItemNum = '4011'
AND it.[datetime] > '05/15/2015'
GROUP BY CAST(Datetime AS DATE)
) as X
This way you can wrap to results into the derived table, and then apply rest of the operations on that.
I would assume this would work too, but can't test now:
SELECT DATENAME(MM, CAST([datetime] AS DATE)) + ' '
+ CAST(DAY(CAST([datetime] AS DATE)) AS VARCHAR(2)) AS [DD Month],
SUM(quantity) as total_quantity
FROM Invoice_Itemized ii
INNER JOIN Invoice_Totals it ON it.Invoice_Number = ii.Invoice_Number
WHERE ii.ItemNum = '4011'
AND it.[datetime] > '05/15/2015'
GROUP BY CAST([datetime] AS DATE)
I'm struggling with an algorithm in SQL server , and I have no clue how to solve this .
I have a column with timestamps on which I've ordered by desc .
I need to find out the differences between de 2nd row and the 1st , the 3rd row and the 2 nd as so on and display this in a new row .The final table should look like this :
ID Type Time_Stamp Difference
xxx YYY 00:03:12 00:00:02
xxx ZZZ 00:03:14 00:00:02
xxx ZZZ 00:03:16
Can I use some kind of SQL function ? Please let me know if you have any ideas .
Cheers ,
Use Window Function
;WITH cte
AS (SELECT Row_number()
OVER(
ORDER BY time_stamp) rn,
*
FROM yourtable)
SELECT a.ID,
a.Type,
a.Time_Stamp,
CONVERT(VARCHAR(10), Datediff(second, a.Time_Stamp, b.Time_Stamp)/3600)
+ ':'
+ RIGHT('00'+CONVERT(VARCHAR(2), (Datediff(second, a.Time_Stamp, b.Time_Stamp)%3600)/60), 2)
+ ':'
+ RIGHT('00'+CONVERT(VARCHAR(2), Datediff(second, a.Time_Stamp, b.Time_Stamp)%60), 2) AS [Difference]
FROM cte a
LEFT JOIN cte b
ON a.rn = b.rn - 1
If you are using Sql Server 2012+ then use Lead Function
;WITH cte
AS (SELECT *,
Datediff(second, time_stamp, Lead(time_stamp)
OVER(
ORDER BY time_stamp)) AS Sec
FROM yourtable)
SELECT a.ID,
a.Type,
a.Time_Stamp,
CONVERT(VARCHAR(10), sec/3600) + ':'
+ RIGHT('00'+CONVERT(VARCHAR(2), (sec%3600)/60), 2)
+ ':'
+ RIGHT('00'+CONVERT(VARCHAR(2), sec%60), 2) AS [Difference]
FROM cte a
Update : To insert into temp table do this.
;WITH cte
AS (SELECT Row_number()
OVER(
ORDER BY time_stamp) rn,
*
FROM yourtable)
SELECT a.ID,
a.Type,
a.Time_Stamp,
CONVERT(VARCHAR(10), Datediff(second, a.Time_Stamp, b.Time_Stamp)/3600)
+ ':'
+ RIGHT('00'+CONVERT(VARCHAR(2), (Datediff(second, a.Time_Stamp, b.Time_Stamp)%3600)/60), 2)
+ ':'
+ RIGHT('00'+CONVERT(VARCHAR(2), Datediff(second, a.Time_Stamp, b.Time_Stamp)%60), 2) AS [Difference]
into #tempTable --Here you need to use into temptable
FROM cte a
LEFT JOIN cte b
ON a.rn = b.rn - 1
or create the temp table and use
......
Insert into #temptable
SELECT a.ID,
a.Type,
a.Time_Stamp
......
SAMPLE TABLE
CREATE TABLE #TEMP(ID VARCHAR(10),[TYPE] VARCHAR(10),TIME_STAMP TIME)
INSERT INTO #TEMP
SELECT 'xxx' , 'YYY', '00:03:12'
UNION ALL
SELECT 'xxx', 'ZZZ', '00:03:14'
UNION ALL
SELECT 'xxx', 'ZZZ', '00:03:16'
You need to do self join logic to get next row's record
QUERY
;WITH CTE AS
(
SELECT ROW_NUMBER()OVER(ORDER BY TIME_STAMP)RNO,*
FROM #TEMP
)
SELECT C1.*,'00:00:'+CAST(DATEDIFF(S,C1.TIME_STAMP,C2.TIME_STAMP)AS VARCHAR(2)) D
FROM CTE C1
LEFT JOIN CTE C2 ON C1.RNO=C2.RNO-1
Click here to view result