I have several database objects which need to extract a single record (like TOP 1) from a table, but the priority for which one is chosen depends on a BIT value in a settings table, and that settings table will contain only one row.
I have written a view which will perform the required functionality:
CREATE VIEW TopOrganisationAddresses AS
WITH cte AS
(
SELECT OrganisationID,
AddressID,
CASE WHEN EXISTS (SELECT * FROM GlobalSettings WHERE DeliveryAddressInReports=1) THEN IsDeliveryAddress ELSE IsInvoiceAddress END AS OrderFirst,
CASE WHEN EXISTS (SELECT * FROM GlobalSettings WHERE DeliveryAddressInReports=1) THEN IsInvoiceAddress ELSE IsDeliveryAddress END AS OrderSecond
FROM OrganisationAddresses
)
SELECT OrganisationID, AddressID,
ROW_NUMBER() OVER(PARTITION BY OrganisationID
ORDER BY OrderFirst DESC, OrderSecond DESC) AS [Row]
FROM cte
Will the SELECT * FROM GlobalSettings queries be evaluated for every fow in the OrganisationAddresses table? If so this would be incredibly wasteful as it is only a static value that isn't going to change.
In theory it could be optimised away but you can perhaps just use another cte which does that SELECT to guarantee it e.g.
CREATE VIEW TopOrganisationAddresses AS
WITH cteg AS
(
SELECT CASE WHEN EXISTS (SELECT * FROM GlobalSettings WHERE DeliveryAddressInReports=1) THEN 1 ELSE 0 END AS dair
),
cte AS
(
SELECT OrganisationID,
AddressID,
CASE WHEN cteg.dair = 1 THEN IsDeliveryAddress ELSE IsInvoiceAddress END AS OrderFirst,
CASE WHEN cteg.dair = 1 THEN IsInvoiceAddress ELSE IsDeliveryAddress END AS OrderSecond
FROM OrganisationAddresses
CROSS JOIN cteg
)
SELECT OrganisationID, AddressID,
ROW_NUMBER() OVER(PARTITION BY OrganisationID
ORDER BY OrderFirst DESC, OrderSecond DESC) AS [Row]
FROM cte
SQL Server should be smart enough to run the queries only once. The optimizer should recognize exactly what you do -- that these are constant values. For instance, the following query returns the same value for the id:
with cte as (
select v.*,
(select top 1 newid() from (values (1), (2)) v2(n) order by newid()) as val
from (values (1), (2)) v(n)
)
select *
from cte;
(Here is a db<>fiddle.)
If you wanted to be sure that these are evaluated only once, you can move the logic to the FROM clause:
CREATE VIEW TopOrganisationAddresses AS
SELECT oa.OrganisationID, oa.AddressID,
ROW_NUMBER() OVER (PARTITION BY oa.OrganisationID
ORDER BY v.OrderFirst DESC, v.OrderSecond DESC
) AS [Row]
FROM OrganisationAddresses oa CROSS JOIN
(VALUES (CASE WHEN EXISTS (SELECT * FROM GlobalSettings WHERE DeliveryAddressInReports = 1) THEN IsDeliveryAddress ELSE IsInvoiceAddress END,,
CASE WHEN EXISTS (SELECT * FROM GlobalSettings WHERE DeliveryAddressInReports = 1) THEN IsInvoiceAddress ELSE IsDeliveryAddress END)
) v(OrderFirst, OrderSecond);
Related
I'm trying to optimize the filtering of data in one report/table and I've encountered a challenge.
Table is located in m.access, so any vba access code or sql query should work here.
So far I've tried few options, but could not achieve expected results:
select prev_type, type, next_type
from (
select *,
lag(type) over (order by id) as prev_type,
type,
lead(type) over (order by id) as next_type
from table
) as t
where type = "type";
Basically I want to display from below table three rows:
row with Type = 'D'
previous row to the one with Type 'D'
next row to the one with Type 'D'
enter image description here
Try with a subquery:
Select * From YourTable
Where Abs([ID] - (Select ID From YourTable Where [Type] = 'D')) <= 1
For multiple Ds, join the subquery:
Select
*
From
YourTable ,
(Select ID From YourTable Where [Type] = 'D') As T
Where
Abs(YourTable.[ID] - T.ID) <= 1
I have been helped by Metal to write a SQL as below
select id
, OrderDate
, RejectDate
, max(case when RejectDate = '1900-01-01' then '9999-12-31' else RejectDate end) as rSum
from tableA
group by id, OrderDate, RejectDate
Now, I would like to find out all the records for a partcular id below the max reject date to delete them from a transformation table
An option is to use row_number():
select
id,
OrderDate,
RejectDate
from (
select
t.*,
row_number() over(
partition by id
order by case when RejectDate = '1900-01-01' then '9999-12-31' else RejectDate end desc
) rn
from tableA t
) t
where rn > 1
The advantage of this technique is that it avoids aggregation, which may lead to better performance. Also, you can easily turn this into a delete statement by leveraging the concept of updateable CTE, as follows:
with cte as (
select
row_number() over(
partition by id
order by case when RejectDate = '1900-01-01' then '9999-12-31' else RejectDate end desc
) rn
from tableA t
)
delete from cte where rn > 1
This should work...
SELECT *
FROM tableA t1
INNER JOIN (
SELECT ID, MAX(RejectDate) as MaxRejectDate
FROM tableA) t2 ON t1.ID = t2.ID
WHERE t1.RejectDate < t2.MaxRejectDate
I am working with a set of data in a table.
For simplicity i have the table like below with some sample data:
Some of the data in this table came from a different source, such data are the ones that have cqmRecordID != null
I need to find duplicate values in this table and delete the duplicate ones that came over from the other source (ones with a cqmRecordID)
A record is considered duplicate if they have the same values for these cols:
[Name]
Cast([CreatedDate] as Date)
[CreatedBy]
So in the sample data i have above, record #5 and record #6 would be considered duplicates.
As solutions I came up with these two queries:
Query #1:
select * from (
select recordid, cqmrecordid, ROW_NUMBER() over (partition by name, cast(createddate as date), createdby
order by cqmrecordid, recordid) as rownum
from vmsNCR ) A
where cqmrecordid is not null
order by recordid
Query #2:
select A.recordID, A.cqmRecordID, B.RecordID, B.cqmRecordID
from vmsNCR A
join vmsNCR B
on A.Name = B.Name
and cast(A.CreatedDate as date) = cast(B.CreatedDate as date)
and A.CreatedBy = B.CreatedBy
and A.RecordID != B.RecordID
and A.cqmRecordID is not null
order by A.RecordID
Is there a better approach to this? Is one better than the other performance wise?
If you want to fetch all the rows without duplicates, then:
select t.* -- or all columns except seqnum
from (select t.*,
row_number() over (partition by name, cast(createddate as date), createdby
order by (case when cqmRecordId is not null then 1 else 2 end)
) as seqnum
from t
) t
where seqnum = 1;
If you want performance, create a columns and then an index:
alter table t add cqmRecordId_flag as (case when cqmRecordId is null then 0 else 1 end) persisted;
alter table t add createddate_date as (cast(createddate as date)) persisted;
And then an index:
create index idx_t_4 on t(name, createddate_date, createdby, cqmRecordId_flag desc);
EDIT:
If you actually just want to delete the NULL values from the table, you can use:
delete t from t
where t.cqmRecordId is null and
exists (select 1
from t t2
where t2.name = t.name and
convert(date, t2.createddate_date) =convert(date, t.createddate_date) and
t2.createdby = t.createdby and
t2.cqmRecordId is not null
);
You can use the same logic with select to just select the duplicates.
Try below Query it might work for You
;WITH TestCTE
AS
(
SELECT *,ROW_NUMBER() OVER(
PARTITION BY [Name],Cast([CreatedDate] as Date),[CreatedBy]
ORDER BY RecordId
) AS RowNumber
)
DELETE FROM TestCTE
WHERE RowNumber > 1
Use the below code to eliminate duplicates
;WITH CTE
AS
(
SELECT ROW_NUMBER() OVER(
PARTITION BY [Name],Cast([CreatedDate] as Date),[CreatedBy]
ORDER BY cqmRecordId
) AS Rnk
,*
)
DELETE FROM CTE
WHERE Rnk <> 1
this sounds like a simple question but I just cant find the right way.
given the simplified table
with t as (
select ordernumber, orderdate, case when ordertype in (5,21) then 1 else 0 end is_restore , ordertype, row_number() over(order by orderdate) rn from
(
select to_date('29.08.08','DD.MM.YY') orderdate,'313' ordernumber, 1 as ordertype from dual union all
select to_date('13.03.15','DD.MM.YY') orderdate, '90/4/2' ordernumber, 5 as ordertype from dual
)
)
select * from t -- where clause should be here
for every row is_restore guaranteed to be 1 or 0.
if table has a row where is_restore=1 then select ordernumber,orderdate of that row and nothing else.
If a table does not have a row where is_restore=1 then select ordernumber,orderdate of the row where rn=1(row where rn=1 is guaranteed to exist in a table)
Given the requirements above what do I need to put in where clause to get the following?
You could use ROW_NUMBER:
CREATE TABLE t
AS
select ordernumber, orderdate,
case when ordertype in (5,21) then 1 else 0 end is_restore, ordertype,
row_number() over(order by orderdate) rn
from (
select to_date('29.08.08','DD.MM.YY') orderdate,'313' ordernumber,
1 as ordertype
from dual union all
select to_date('13.03.15','DD.MM.YY') orderdate, '90/4/2' ordernumber,
5 as ordertype
from dual);
-------------------
with cte as (
select t.*,
ROW_NUMBER() OVER(/*PARTITION BY ...*/ ORDER BY is_restore DESC, rn) AS rnk
from t
)
SELECT *
FROM cte
WHERE rnk = 1;
db<>fiddle demo
Here is sql, that doesn't use window functions, maybe it will be useful for those, whose databases don't support OVER ( ... ) or when there are indexed fields, on which query is based.
SELECT
*
FROM t
WHERE t.is_restore = 1
OR (
NOT EXISTS (SELECT 1 FROM t WHERE t.is_restore = 1)
AND t.rn = 1
)
I need a temporary solution to a problem I've created. Essentially I want to calculate two values, but use a different method dependant on the result of a condition.
select userReturnval, userregisterid, OtherValue
FROM
(
(SELECT otherValue
FROM...
) as tblO --unrelated table
,
(
if (select count(userregisterid) from table1 where site =#siteID and userid=#userID) >0
SELECT userReturnval, userregisterid
FROM
(
SELECT userReturnval, userregisterid, Rank() OVER (PARTITION BY .. ORDER BY ...) as RANK
FROM ...
WHERE --first where clause
) as tblRank
WHERE (RANK =1)
else
SELECT userReturnval, userregisterid
FROM
(
SELECT userReturnval, userregisterid, Rank() OVER (PARTITION BY .. ORDER BY ...) as RANK
FROM ...
WHERE --different where clause
) as tblRank
WHERE (RANK =1)
) as tblR
My if works fine on its own, I just to get it working as part of the larger query. At the moment, sqlserver doesn't like the if being in there.
Hopefully someone can point me in the right direction!
You can try using case statements in the WHERE clause, something like the statement below. Note that I don't think this will be particularly optimal for performance.
Doing it like this does allow you to keep it to a single statement though:
select userReturnval, userregisterid, OtherValue
FROM
(
(SELECT otherValue
FROM...
) as tblO --unrelated table
,
(
SELECT userReturnval, userregisterid
FROM
(
SELECT userReturnval, userregisterid, Rank() OVER (PARTITION BY .. ORDER BY ...) as RANK
FROM ...
WHERE
case --Choose which where clause to use
when (select count(userregisterid) from table1 where site =#siteID and userid=#userID) >0 then
case when /*First where clause*/ then 1
else 0
end
else
case when /*Second where clause*/ then 1
else 0
end
end
= 1
) as tblRank
WHERE (RANK =1)
) as tblR