If else exist statement in select query - sql

Here are three tables where I want a query to select which filter/s will be expired in 7 next few days.
I have 2 columns called filterExp in the Filter table and filterExpCustom in the FilterChange table, the first is the default if the second is not empty.
All filters have an expiration value in the day terms but in some cases, some users want to change the expiration value for their own. So they can set filterExpCustom for specific customers and filters.
Here are some actual values to better understand:
Filter
INSERT [Filter] ([filterId], [filterName], [filterExp])
VALUES (1, N'pp', 6)
INSERT [Filter] ([filterId], [filterName], [filterExp])
VALUES (2, N'Carbonate', 5)
INSERT [Filter] ([filterId], [filterName], [filterExp])
VALUES (3, N'Carbon Block', 5)
INSERT [Filter] ([filterId], [filterName], [filterExp])
VALUES (4, N'Carbon Post', 12)
INSERT [Filter] ([filterId], [filterName], [filterExp])
VALUES (5, N'Mineral', 12)
FilterChange
INSERT [FilterChange] ([filterChnageId], [customerId], [filterId], [customerDeviceId], [filterChangeDate], [filterExpCustom])
VALUES (186, 3, 2, 65, CAST(N'2023-01-31' AS Date), 7)
INSERT [FilterChange] ([filterChnageId], [customerId], [filterId], [customerDeviceId], [filterChangeDate], [filterExpCustom])
VALUES (187, 3, 5, 65, CAST(N'2023-01-31' AS Date), NULL)
INSERT [FilterChange] ([filterChnageId], [customerId], [filterId], [customerDeviceId], [filterChangeDate], [filterExpCustom])
VALUES (188, 2, 3, 66, CAST(N'2023-02-01' AS Date), 10)
INSERT [FilterChange] ([filterChnageId], [customerId], [filterId], [customerDeviceId], [filterChangeDate], [filterExpCustom])
VALUES (189, 2, 3, 66, CAST(N'2023-02-01' AS Date), NULL)
Here is the query that I can get the expired filter/s from now until 7 days next:
CREATE PROCEDURE [dbo].[FilterExpirationGetList]
#userName BIGINT
AS
DECLARE #InNextDays int
BEGIN
SET #InNextDays = 7
SELECT f.filterId,f.filterName, f.filterExp,
fc.customerId, fc.customerDeviceId, fc.filterChangeDate, fc.filterExpCustom,
c.fName, c.lName, c.cMobile, c.userName
FROM FilterChange fc INNER JOIN
Filter f ON fc.filterId = f.filterId INNER JOIN
Customer c ON c.CustomerId = fc.customerId
WHERE c.userName = #userName AND
DATEADD(DAY, DATEPART(DAY, GETDATE())
- DATEPART(DAY, DATEADD(DAY,f.filterExp,fc.filterChangeDate)),
DATEADD(DAY,f.filterExp,fc.filterChangeDate))
BETWEEN CONVERT(DATE, GETDATE())
AND CONVERT(DATE, GETDATE() + #InNextDays);
END
The problem with my query is I don't know how to select expired filters if filterExpCustom has some value but if it doesn't, the filterExp must be calculated in the query.

Something like this perhaps, you use ISNULL to get the correct day extension:
select *
from customer c
inner join FilterChange fc
ON fc.customerID = fc.CustomerID
INNER JOIN Filter f
ON f.filterID = fc.filterID
WHERE DATEADD(DAY, ISNULL(fc.filterExpCustom,filterExp), filterChangeDate) BETWEEN CAST(GETDATE() AS DATE) AND DATEADD(DAY, 7, GETDATE())
I'm not sure what you do with customers where filters expired before today though. Your design seem lacking a sort of flag that says that filterchange is "superceeded" with another change.
Alternatively you can fetch only the latest filterchange per filter type where expiration dates is less than 7 days forward. I leave it as exercise to the reader

Related

How to avoid duplication while trying to display employee work details consisting of start & end dates, hours worked and hourly rate in SQL Server?

Below is the table I have created and inserted values in it:
CREATE TABLE employees_list
(
employeeID int identity(1,1),
employeeName varchar(25)
)
GO
INSERT INTO employees_list VALUES ('Kevin'),('Charles')
GO
CREATE TABLE hourlyRates
(
employeeID int,
rate int,
rateDate date
)
INSERT INTO hourlyRates VALUES (1, 28, '2016-01-01'),
(1, 39, '2016-02-01'),
(2, 43, '2016-01-01'),
(2, 57, '2016-02-01')
CREATE TABLE workingHours
(
employeeID int,
startdate datetime,
enddate datetime
)
GO
INSERT INTO workingHours VALUES (1, '2016-01-01 09:00', '2016-01-01 17:00'),
(1, '2016-01-02 09:00', '2016-01-02 17:00'),
(1, '2016-02-01 10:00', '2016-02-01 16:00'),
(1, '2016-02-02 11:00', '2016-02-02 13:00'),
(2, '2016-01-01 10:00', '2016-01-01 16:00'),
(2, '2016-01-02 08:00', '2016-01-02 14:00'),
(2, '2016-02-01 14:00', '2016-02-01 19:00'),
(2, '2016-02-02 13:00', '2016-02-02 16:00')
GO
SELECT * FROM employees_list
SELECT * FROM hourlyRates
SELECT * FROM workingHours
Now the question is:
Display employee ID, name, start date, end date, hours worked and hourly rate for the Employee whose ID number is 1.
This is what I have done:
SELECT
workingHours.employeeID, employeeName,
startdate, enddate,
DATEDIFF(HOUR, startdate, enddate) AS 'Hours Worked',
rate AS 'Hourly Rate'
FROM
hourlyRates, workingHours, employees_list
WHERE
hourlyRates.employeeID = workingHours.employeeID
AND employees_list.employeeID = workingHours.employeeID
AND workingHours.employeeID = 1
And I got the following result:
The problem with the result above is that the result is being repeated or duplicated from row number 5 to row number 8.
It is supposed to generate the first 4 rows if I'm not mistaken.
I even tried adding DISTINCT in the SELECT statement and still it is showing duplicated result.
What change is needed on my query to eliminate the duplication?
The problem is you're joining the tables only on the employeeID. With that, you will get a row for every combination of hourlyRates and workingHours - which is 8 in this case. You'd have to join the tables somehow on the dates as well. You want to take a rate from hourlyRates only when it's in the correct month. The simplest way to do that with your query would be adding another join condition:
SELECT workingHours.employeeID,employeeName,startdate,enddate,
DATEDIFF(HOUR,startdate,enddate) AS 'Hours Worked',
rate AS 'Hourly Rate'
FROM hourlyRates,workingHours,employees_list
WHERE hourlyRates.employeeID = workingHours.employeeID
AND employees_list.employeeID = workingHours.employeeID
AND (hourlyRates.rateDate
BETWEEN
DATEFROMPARTS(DATEPART(YEAR, workingHours.startDate), DATEPART(MONTH,workingHours.startDate), 1)
AND DATEFROMPARTS(DATEPART(YEAR, workingHours.endDate), DATEPART(MONTH,workingHours.endDate), 1))
AND workingHours.employeeID = 1
The new join condition is taking year and month parts with the DATEPART function, from workingHours's dates, and making sure that hourlyRates.rateDate is between those two.
DATEFROMPARTS function takes year, month and day integers (for the start of the month, we take that day is 1) and converts it to DATE type.
There are a couple of problems with the above query however. The obvious one being the syntax. You should use the new join syntax. The other problem is that the query is not Sargable because of the function calls on column values in the join condition. These queries should be avoided when possible.
There are a couple of ways of making a query sargable. For example we could insert the processed dates into a temp table, and use it for the query:
SELECT wh.employeeID
,DATEFROMPARTS(DATEPART(YEAR, wh.startDate), DATEPART(MONTH,wh.startDate), 1) AS startDateYearMonth
,DATEFROMPARTS(DATEPART(YEAR, wh.endDate), DATEPART(MONTH,wh.endDate), 1) AS endDateMonthYearMonth
,wh.startdate
,wh.enddate
INTO #TempWorkingHours
FROM workingHours wh
SELECT el.employeeID
,el.employeeName
,twh.startdate
,twh.enddate
,DATEDIFF(HOUR,twh.startdate,twh.enddate) AS [Hours Worked]
,hr.rate AS [Hourly Rate]
FROM employees_list el
JOIN hourlyRates hr ON el.employeeID = hr.employeeID
JOIN #TempWorkingHours twh ON el.employeeID = twh.employeeID
AND (hr.rateDate
BETWEEN
twh.startDateYearMonth
AND twh.endDateMonthYearMonth
)
WHERE el.employeeID = 1
Note that this will not improve performance (will even make it worse because of the temp table overhead) if you don't use indexes on the pre-processed columns. Without indexes, a non-sargable query will be fine, and with the new join syntax would look like this:
SELECT el.employeeID
,el.employeeName
,wh.startdate
,wh.enddate
,DATEDIFF(HOUR,wh.startdate,wh.enddate) AS [Hours Worked]
,hr.rate AS [Hourly Rate]
FROM employees_list el
JOIN hourlyRates hr ON el.employeeID = hr.employeeID
JOIN workingHours wh ON hr.employeeID = wh.EmployeeID
AND (hr.rateDate
BETWEEN
DATEFROMPARTS(DATEPART(YEAR, wh.startDate), DATEPART(MONTH,wh.startDate), 1)
AND DATEFROMPARTS(DATEPART(YEAR, wh.endDate), DATEPART(MONTH,wh.endDate), 1)
)
WHERE wh.employeeID = 1

How to get one output table from select in while-loop

Edited to live up to the rules here. Sorry about my first attempt.
I got the following sample data:
CREATE TABLE SampleData(
[Time] [time](7) NOT NULL
) ON [PRIMARY]
GO
INSERT INTO SampleData([Time]) VALUES ('01:00:00')
INSERT INTO SampleData([Time]) VALUES ('02:00:00')
INSERT INTO SampleData([Time]) VALUES ('02:00:00')
INSERT INTO SampleData([Time]) VALUES ('03:00:00')
INSERT INTO SampleData([Time]) VALUES ('03:00:00')
INSERT INTO SampleData([Time]) VALUES ('03:00:00')
INSERT INTO SampleData([Time]) VALUES ('04:00:00')
INSERT INTO SampleData([Time]) VALUES ('04:00:00')
INSERT INTO SampleData([Time]) VALUES ('04:00:00')
INSERT INTO SampleData([Time]) VALUES ('04:00:00')
GO
This is my query:
DECLARE #Counter INT
SET #Counter = 1
WHILE (#Counter <= 4)
BEGIN
SELECT Count([Time]) AS OrdersAmount
FROM SampleData
WHERE DATEPART(HOUR, [Time]) = #Counter
SET #Counter = #Counter + 1
END
This is the result of the query:
OrdersAmount
1
-----
OrdersAmount
2
-----
OrdersAmount
3
-----
OrdersAmount
4
So 4 seperate tables. What I need is one table, with alle values in it, on each their own row, like this:
OrdersAmount
1
2
3
4
I tried with cte and declaring a temp table, but I just can't make it work.
I don't have the data so can't test.
But if I get your problem right, this should work for you.
select PromisedPickupDt = cast(PromisedPickupDate as date),
[Hour] = datepart(hour, PromisedPickupDate),
HourlyAmount = sum(OrdersAmount)
from [FLX].[dbo].[NDA_SAP_Bestillinger]
where cast(PromisedPickupDate as date) = cast(getdate() as date)
group by cast(PromisedPickupDate as date), datepart(hour, PromisedPickupDate)
You need an Hours table to join and group against. You can create this using a VALUES constructor:
SELECT
Count(*) AS OrdersAmount
FROM (VALUES
(0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15),(16),(17),(18),(19),(20),(21),(22),(23)
) AS v(Hour)
LEFT JOIN [FLX].[dbo].[NDA_SAP_Bestillinger]
ON v.Hour = DATEPART(hour, PromisedPickupTime)
AND PromisedPickupDate >= CAST(CAST(GETDATE() AS date) AS datetime)
AND PromisedPickupDate < CAST(DATEADD(day, 1, CAST(GETDATE() AS date)) AS datetime)
GROUP BY v.Hour;
What is going on here is that we start with a filter by the current date.
Then we join a constructed Hours table against the hour-part of the date. Because it is on the left-side of a LEFT JOIN, we now have all the hours whether or not there is any value in that hour. We can now group by it.
Note the correct method to compare to a date range: a half-open interval >= AND <
Do not use YEAR\MONTH etc in join and filter predicates, as indexes cannot be used for this.

Perform a left join based on one to multiple rows in both tables in SQL Server

One table contains codes representing parts of surgical operations. One procedure is described by one ore more codes (one row per code). Combinations of procedures may occure. One operation may e.g.consist of 1 procedure represented by one code and a second procedure represented by 3 codes (4 rows for this operation).
A left join has to be performed with a second table containing the codes for the procedures with one or many codes each in one row.
Of note, a certain code used to describe a procedure based on a total of three codes may also occure as a single code describing a different procedure like the last entry in table #Procedures. Hence, a simple join does not solve the problem.
In the present solution I use two nested while statement taking hours in SQL Server 2014. Is there a more efficiant way to perform this task, maybe using the xml functionality of SQL Server?
Any help appreciated.
Example:
CREATE TABLE #Procedures
(
ProcID INT,
NumberofCodes INT,
Codes NVARCHAR(20)
)
INSERT INTO #Procedures (ProcID, NumberofCodes, Codes)
VALUES (1, 1, 'SingleCodeXYZ'), (2, 3, 'TripleCodeXY1'), (2, 3, 'TripleCodeXY2'),
(2, 3, 'TripleCodeXY3'), (3, 2, 'DoubleCodeXY1'), (3, 2, 'DoubleCodeXY2'),
(4, 1, 'SingleCodeABC'), (5, 1, 'TripleCodeXY2')
CREATE TABLE #KodesforOperations
(
OPID INT,
ProcID INT,
NumberofCodes INT,
Codes NVARCHAR(20)
)
INSERT INTO #KodesforOperations (OPID, NumberofCodes, Codes)
VALUES (1, 4, 'SingleCodeXYZ'), (1, 4, 'TripleCodeXY1'), (1, 4, 'TripleCodeXY2'),
(1, 4, 'TripleCodeXY3'),
(2, 1, 'SingleCodeABC'),
(3, 2, 'DoubleCodeXY1'), (3, 2, 'DoubleCodeXY2')
--SELECT * FROM #Procedures
--SELECT * FROM #KodesforOperations
--nested While statements:
UPDATE A
SET A.ProcID = B.ProcID
FROM #KodesforOperations A
LEFT JOIN #Procedures B On A.Codes = B.Codes
WHERE A.NumberofCodes >= 3 AND B.NumberofCodes >= 3
UPDATE A
SET A.ProcID = B.ProcID
FROM #KodesforOperations A
LEFT JOIN #Procedures B On A.Codes = B.Codes
WHERE A.NumberofCodes >= 2 AND B.NumberofCodes >= 2
UPDATE A
SET A.ProcID = B.ProcID
FROM #KodesforOperations A
LEFT JOIN #Procedures B On A.Codes = B.Codes
WHERE A.NumberofCodes >= 1 AND B.NumberofCodes >= 1
SELECT * FROM #KodesforOperations
You should be able to combine the conditions. However, this is logically equivalent to just checking the third:
UPDATE A
SET A.ProcID = B.ProcID
FROM #KodesforOperations A JOIN
#Procedures B
On A.Codes = B.Codes
WHERE A.NumberofCodes >= 1 AND B.NumberofCodes >= 1;
If this condition is true, then the comparisons to 2 and 3 are also true.
The LEFT JOIN is unnecessary, because you need a match in the two tables to test the WHERE condition.

SQL Update Or Insert By Comparing Dates

I am trying to do the UPDATE or INSERT, but I am not sure if this is possible without using loop. Here is the example:
Says, I have this SQL below in which I joined two tables: tblCompany and tblOrders.
SELECT CompanyID, CompanyName, c.LastSaleDate, o.SalesOrderID, o.SalesPrice
, DATEADD(m, -6, GETDATE()) AS DateLast6MonthFromToday
FROM dbo.tblCompany c
CROSS APPLY (
SELECT TOP 1 SalesOrderID, SalesPrice
FROM dbo.tblOrders o
WHERE c.CompanyID = o.CompanyID
ORDER BY SalesOrderID DESC
) AS a
WHERE Type = 'End-User'
Sample Result:
CompanyID, SalesOrderID, SalesPrice, LastSalesDate, DateLast6MonthFromToday
101 10001 50 2/01/2016 10/20/2016
102 10002 80 12/01/2016 10/20/2016
103 10003 80 5/01/2016 10/20/2016
What I am trying to do is comparing the LastSalesDate and the DateLast6MonthFromToday. Condition is below:
If the LastSalesDate is lesser (earlier), then do the INSERT INTO tblOrders (CompanyID, Column1, Column2...) VALUES (CompanyIDFromQuery, Column1Value, Column2Value)
Else, do UPDATE tblOrders SET SalesPrice = 1111 WHERE SalesOrderID = a.SalesOrderID
As the above sample result, the query will only update SalesOrderID 10001 and 10003. And For Company 102, NO insert since the LastSaleDate is greater, then just do the UPDATE for the SalesOrderID.
I know it is probably can be done if I create a Cursor to loop through every record and do the comparison then Update or Insert, but I wonder if there is another way perform this without the loop since I have around 20K records.
Sorry for the confusion,
I don't know your tables structure and your data types. Also I know nothing
about duplicates and join ralationships between this 2 tables.
But I want only show how it works on next example:
use [your test db];
go
create table dbo.tblCompany
(
companyid int,
companyname varchar(max),
lastsaledate datetime,
[type] varchar(max)
);
create table dbo.tblOrders
(
CompanyID int,
SalesOrderID int,
SalesPrice float
);
insert into dbo.tblCompany
values
(1, 'Avito', '2016-01-01', 'End-User'),
(2, 'BMW', '2016-05-01', 'End-User'),
(3, 'PornHub', '2017-01-01', 'End-User')
insert into dbo.tblOrders
values
(1, 1, 500),
(1, 2, 700),
(1, 3, 900),
(2, 1, 500),
(2, 2, 700),
(2, 3, 900),
(3, 1, 500),
(3, 2, 700),
(3, 3, 900)
declare #column_1_value int = 5;
declare #column_2_value int = 777;
with cte as (
select
CompanyID,
SalesOrderID,
SalesPrice
from (
select
CompanyID,
SalesOrderID,
SalesPrice,
row_number() over(partition by CompanyID order by SalesOrderId desc) as rn
from
dbo.tblOrders
) t
where rn = 1
)
merge cte as target
using (select * from dbo.tblCompany where [type] = 'End-User') as source
on target.companyid = source.companyid
and source.lastsaledate >= dateadd(month, -6, getdate())
when matched
then update set target.salesprice = 1111
when not matched
then insert (
CompanyID,
SalesOrderID,
SalesPrice
)
values (
source.CompanyId,
#column_1_value,
#column_2_value
);
select * from dbo.tblOrders
If you will give me an information, then I can prepare target and source tables properly.

SQL update one single row table with data from another

I have two tables. The first one with all movements in twelve months and the second one with claims registered in the same period of time. When I run the following query from the first table I've got 10 records. Of course, there are other records with a different number of movements (e.g.: 7, 23, 2 movements):
select t.cod_suc
,t.cod_ramo_comercial
,t.Poliza
,t.Item
,t.id_pv
from temp_portafolio_personal_accidents as t
where t.cod_suc = 2
and t.cod_ramo_comercial = 46
and t.Poliza = 50283
and t.Item = 1
and t.id_pv = 788383;
With the second query, for the second table, I have the following results:
select c.cod_suc
,c.cod_ramo_comercial
,c.[No. Policy]
,c.Item
,c.[ID Incident]
,max(c.id_pv) as id_pv
,count(distinct [No. Incident]) as 'Conteo R12'
from #claims as c
where c.[ID Incident] = 343632
group by c.cod_suc
,c.cod_ramo_comercial
,c.[No. Policy]
,c.Item
,c.[ID Incident];
Now, I need to update the first table but only one record. I'm using the following query, but all records are being updated. When I sum results I have 10 but is just one claim, as the second query shows.
update p
set [No. Siniestros R12] = b.[Conteo R12]
from temp_portafolio_personal_accidents p
left join
(select c.cod_suc
,c.cod_ramo_comercial
,c.[No. Policy]
,c.Item
,c.[ID Incident]
,max(c.id_pv) as id_pv
,count(distinct [No. Incident]) as 'Conteo R12'
from
#claims as c
where c.[ID Incident] = 343632
group by c.cod_suc
,c.cod_ramo_comercial
,c.[No. Policy]
,c.Item
,c.[ID Incident]
) b
on p.id_pv = b.id_pv
and p.cod_suc = b.cod_suc
and p.cod_ramo_comercial = b.cod_ramo_comercial
and p.Poliza = b.[No. Policy]
and p.Item = b.Item
where p.id_pv = 788383;
You can use a CTE with a ROW_NUMBER() function to do this. Simple example:
DECLARE #TABLE AS TABLE (Testing INT, Testing2 VARCHAR(55), Testing3 BIT);
INSERT INTO #TABLE VALUES (1, '1', 1);
INSERT INTO #TABLE VALUES (1, '1', 1);
INSERT INTO #TABLE VALUES (1, '1', 1);
INSERT INTO #TABLE VALUES (1, '1', 1);
INSERT INTO #TABLE VALUES (1, '1', 1);
INSERT INTO #TABLE VALUES (1, '1', 1);
INSERT INTO #TABLE VALUES (1, '1', 1);
INSERT INTO #TABLE VALUES (1, '1', 1);
WITH CTE AS
(
SELECT
ROW_NUMBER() OVER (ORDER BY Testing) AS RowID
,Testing
,Testing2
,Testing3
FROM #TABLE
)
UPDATE CTE
SET Testing = 2, Testing2 = '2', Testing3 = 0
WHERE RowID = 1
;
SELECT * FROM #TABLE
;