I want to join a table on 4 conditions, of which 2 are very straightforward and 2 other conditions which are more difficult.
The first two:
PMCCONTRACTSTATUS.ContractID = CUSTAMOUNTREPORTTABLE.ContractID
PMCCONTRACTSTATUS.DATAAREAID = CUSTAMOUNTREPORTTABLE.DATAAREAID
The second two:
Only if the FROMDATE from Table PMCCONTRACTSTATUS is lower then the PERIODSTART in Table CUSTAMOUNTREPORTTABLE
AND only if that is the case THEN Take the highest RowNumber. EDIT: Because I want to JOIN the [STATUS] that is the CLOSEST to the PERIODSTART.
EDIT: I Made a sample dataset which only includes the necessary parts to solve the problem.
CREATE TABLE PMCCONTRACTSTATUSLOG2 (
ContractID nvarchar(20)
,[Status] int
,FromDate date
,RowNumber int
);
CREATE TABLE CUSTAMOUNTREPORTTABLE2 (
ContractID nvarchar(20)
,PERIODSTART date
);
INSERT INTO PMCCONTRACTSTATUSLOG2
(ContractID, [Status], FromDate, RowNumber)
VALUES
('HC1','1','01-01-2019','1'),
('HC1','2','01-02-2019','2'),
('HC1','1','01-04-2019','3'),
('HC2','1','01-04-2019','1'),
('HC2','2','01-05-2019','2'),
('HC3','4','01-01-2019','1'),
('HC3','2','01-02-2019','2'),
('HC3','1','01-07-2019','3'),
('HC3','2','01-09-2019','4'),
('HC4','2','01-08-2019','1'),
('HC4','3','01-07-2019','2'),
('HC5','1','01-02-2019','1');
INSERT INTO CUSTAMOUNTREPORTTABLE2
(ContractID, PERIODSTART)
VALUES
('HC1','01-01-2019'),
('HC2','01-01-2019'),
('HC3','01-01-2019'),
('HC5','01-01-2019'),
('HC1','01-02-2019'),
('HC3','01-02-2019'),
('HC5','01-02-2019'),
('HC1','01-03-2019'),
('HC3','01-03-2019'),
('HC5','01-03-2019'),
('HC1','01-04-2019'),
('HC2','01-04-2019'),
('HC3','01-04-2019'),
('HC5','01-04-2019'),
('HC1','01-05-2019'),
('HC2','01-05-2019'),
('HC3','01-05-2019'),
('HC5','01-05-2019');
SELECT * FROM PMCCONTRACTSTATUSLOG2
SELECT * FROM CUSTAMOUNTREPORTTABLE2
I try with the following query to join the PMCCONTRACTSTATUS Table, I know the logic behind the join, but I fail to write a JOIN that combines a CASE WHEN + SELECT statement.
;WITH PMCCONTRACTSTATUS AS
(
SELECT
CONTRACTID
,[STATUS]
,FROMDATE
,DATAAREAID
,ROW_NUMBER() OVER (PARTITION BY CONTRACTID ORDER BY FROMDATE ASC) RN
FROM PMCCONTRACTSTATUSLOG
WHERE [STATUS] <> 10 OR [STATUS] <> 5
)
SELECT
CART.CONTRACTID
,PMCCONTRACTSTATUS.CONTRACTID
,CART.ACCOUNTNUM
,CART.AMOUNTMSTTOTAL
,CART.PERIODSTART
,PMCCONTRACTSTATUS.FROMDATE
,CART.PERIODEND
,CART.NAME
,CART.PMCCONTRACTSTATUSWEIGHTED
,PMCCONTRACTSTATUS.[STATUS]
FROM CUSTAMOUNTREPORTTABLE CART
LEFT JOIN PMCCONTRACTSTATUS
ON PMCCONTRACTSTATUS.CONTRACTID = CART.CONTRACTID
AND PMCCONTRACTSTATUS.DATAAREAID = CART.DATAAREAID
/*AND CASE WHEN PMCCONTRACTSTATUS.FROMDATE <= CART.PERIODSTART
THEN (SELECT MAX(PMCCONTRACTSTATUSLOG.RN) FROM PMCCONTRACTSTATUSLOG)*/
Any suggestions?
This answer is based from my understanding, maybe some parts are missing :
First I try to join with your 3 first conditions in a CTE. And then I create a ROW_NUMBER() based on the DATEDIFF between your 2 dates.
Tell me if it helps :
;WITH
CTE AS (
SELECT
CART.CONTRACTID
,CART.ACCOUNTNUM
,CART.AMOUNTMSTTOTAL
,CART.PERIODSTART
,PMCCONTRACTSTATUSLOG.FROMDATE
,CART.PERIODEND
,CART.NAME
,CART.PMCCONTRACTSTATUSWEIGHTED
,PMCCONTRACTSTATUSLOG.[STATUS]
,ROW_NUMBER() OVER (PARTITION BY CART.CONTRACTID,CART.DATAAREAID ORDER BY DATEDIFF(second,FROMDATE,PERIODSTART) ASC) RN
FROM CUSTAMOUNTREPORTTABLE CART
LEFT JOIN PMCCONTRACTSTATUSLOG
ON PMCCONTRACTSTATUSLOG.CONTRACTID = CART.CONTRACTID
AND PMCCONTRACTSTATUSLOG.DATAAREAID = CART.DATAAREAID
AND PMCCONTRACTSTATUSLOG.FROMDATE <= CART.PERIODSTART
WHERE [STATUS] NOT IN (5,10)
)
SELECT
CONTRACTID
,ACCOUNTNUM
,AMOUNTMSTTOTAL
,PERIODSTART
,FROMDATE
,PERIODEND
,NAME
,PMCCONTRACTSTATUSWEIGHTED
,[STATUS]
FROM CTE
WHERE RN = 1
I believe you should use OUTER APPLY instead of LEFT JOIN
;WITH PMCCONTRACTSTATUS AS
(
SELECT
CONTRACTID
,[STATUS]
,FROMDATE
,DATAAREAID
,ROW_NUMBER() OVER (PARTITION BY CONTRACTID ORDER BY FROMDATE ASC) RN
FROM PMCCONTRACTSTATUSLOG
WHERE [STATUS] <> 10 OR [STATUS] <> 5
)
SELECT
CART.CONTRACTID
,PMCCONTRACTSTATUS.CONTRACTID
,CART.ACCOUNTNUM
,CART.AMOUNTMSTTOTAL
,CART.PERIODSTART
,MAX_PMCCONTRACTSTATUS.FROMDATE
,CART.PERIODEND
,CART.NAME
,CART.PMCCONTRACTSTATUSWEIGHTED
,MAX_PMCCONTRACTSTATUS.[STATUS]
FROM CUSTAMOUNTREPORTTABLE CART
OUTER APPLY (select top 1 *
from PMCCONTRACTSTATUS
where PMCCONTRACTSTATUS.CONTRACTID = CART.CONTRACTID
AND PMCCONTRACTSTATUS.DATAAREAID = CART.DATAAREAID
AND PMCCONTRACTSTATUS.FROMDATE <= CART.PERIODSTART
ORDER BY PMCCONTRACTSTATUSLOG.RN DESC) as MAX_PMCCONTRACTSTATUS
The table1 has huge rows and field1 is text, so I create a fulltext-index of field1.
I run the sql below, it's very slowly, and the CPU to 100% all the way.
select * from (
select *, ROW_NUMBER() OVER(Order by [createtime] DESC) AS RowId
from table1
where CONTAINS(field1, 'sometext')
) AS t1
where t1.RowId between 1 and 10
I remove RowId query, it becomes fast, less than 1s.
select * from (
select *, ROW_NUMBER() OVER(Order by [createtime] DESC) AS RowId
from table1
where CONTAINS(field1, 'sometext')
) AS t1
Then I think it's about of SQLServer optimization, I try to add zero to RowId query field.
It goes fast! But why?
select * from (
select *, ROW_NUMBER() OVER(Order by [createtime] DESC) AS RowId
from table1
where CONTAINS(field1, 'sometext')
) AS t1
where t1.RowId + 0 between 1 and 10
I have a view operating on a very large table.
CREATE VIEW [myView]
AS
-- first part
WITH A AS (
SELECT MAX(Id), ResultId, LocationId, TeamId
FROM VeryLargeTable
GROUP BY ResultId, LocationId, TeamId)
--second part
SELECT A.Id, A.TeamId, ROW_NUMBER() OVER (PARTITION BY ResultId, LocationId) RN
FROM A
WHERE RN = 1
GO
Now what I run operations such as
SELECT * from [MyView]
WHERE TeamId = 5
This is very slow because it is essentially executing
WITH A AS (
SELECT MAX(Id), ResultId, LocationId, TeamId
FROM VeryLargeTable
GROUP BY ResultId, LocationId, TeamId)
--second part
SELECT A.Id, A.TeamId, ROW_NUMBER() OVER (PARTITION BY ResultId, LocationId) RN
FROM A
WHERE RN = 1 AND TeamId = 5
What I want to know is how to structure this view so it will know to filter for TeamId = 5 in the first part so that, the following code, which is fast, executes
WITH A AS (
SELECT MAX(Id), ResultId, LocationId, TeamId
FROM VeryLargeTable
GROUP BY ResultId, LocationId, TeamId
WHERE TeamId = 5)
--second part
SELECT A.Id, A.TeamId, ROW_NUMBER() OVER (PARTITION BY ResultId, LocationId) RN
FROM A
WHERE RN = 1
You could rewrite your view as Inline TVF:
CREATE FUNCTION dbo.my_func(#TeamId INT)
RETURNS TABLE
AS
RETURN
(
WITH A AS (
SELECT MAX(Id), ResultId, LocationId, TeamId
FROM VeryLargeTable
WHERE TeamId = #TeamId OR #TeamId IS NULL
GROUP BY ResultId, LocationId, TeamId
)
--second part
SELECT A.Id, A.TeamId, ROW_NUMBER() OVER (PARTITION BY ResultId, LocationId) RN
FROM A
WHERE RN = 1
);
And query:
SELECT *
FROM dbo.my_fun(5)
WHERE ...
I have two CTE's,firstly
;WITH CTE AS (SELECT A.*
, Row_NUMBER() Over (Partition by ID order by Date asc) RN
FROM TABLE A)
SELECT Sum(Weight) as IN_WT
FROM CTE
WHERE RN = 1 and name='dev1'
and then
;WITH CTE AS (SELECT B.*
, Row_NUMBER() Over (Partition by ID order by Date desc) RN1
FROM TABLE B)
SELECT Sum(Weight) AS out_wt
FROM CTE
WHERE RN1 = 1 and name='dev1'
Here we had two tablesTableA,TableB.We are getting In_wt from TableA and Out_wt from TableB.Now I had a requiremnt that the output should be combined and get in_wt,out_wt in single row from different tables and same name.I tried combining both the CTE's but didn't get the desired result.How can we do that?
Try this:
;WITH CTE1 AS (
SELECT name,
Row_NUMBER() Over (Partition by ID order by Date asc) RN
FROM TABLEA
), CTE2 AS (
SELECT name,
Row_NUMBER() Over (Partition by ID order by Date desc) RN
FROM TABLEB
)
SELECT (SELECT Sum(Weight) FROM CTE1 WHERE RN = 1 and name='dev1') AS IN_WT,
(SELECT Sum(Weight) FROM CTE2 WHERE RN = 1 and name='dev1') AS OUT_WT
I must be doing something wrong. I'm trying to use the row_number function to only select the values that have a rownum of 1, so that I always get the latest index. However rownum is not recognized in the where-clause.
SELECT fs.docu_id as docID
, fs.field_3 AS ProductNo
, fs.field_4 AS [Status]
, fs.field_5 AS [Index]
,pd.[doku_nr] AS docIDshort
, ROW_NUMBER() OVER(PARTITION BY fs.field_3 ORDER BY fs.field_5 Desc) AS rownum
FROM [table1] fs
JOIN [table2] pd
ON fs.docu_id=pd.docu_id
AND fs.field_4 = 'valid'
What am I missing? Do I need to create a different select stmt?
Thank you.
You cannot use window functions in the Where clause(only in SELECT or ORDER BY).
But you could use a CTE instead:
WITH CTE
AS (SELECT fs.docu_id AS docID,
fs.field_3 AS ProductNo,
fs.field_4 AS [Status],
fs.field_5 AS [Index],
pd.[doku_nr] AS docIDshort,
Row_number() OVER(
Partition BY fs.field_3
ORDER BY fs.field_5 DESC) AS rownum
FROM [table1] fs
JOIN [table2] pd
ON fs.docu_id = pd.docu_id
AND fs.field_4 = 'valid')
SELECT docid, productno, status, [index], docIDshort
FROM CTE
WHERE rownum = 1