I want to update a column using PL/SQL - sql

Here is a query. I want bookdate_to to be updated.
SELECT
r.resource_id,
r.type,
r.resort_id,
r.parent_id,
r.code,
r.name,
r.path,
r.cashflowmanager_id,
c.bookdate_from,
c.bookdate_to,
c.usage_date_from,
c.usage_date_to
FROM
resourcebasei18n r
JOIN cashflowrule c ON ( r.cashflowmanager_id = c.cashflowmanager_id )
WHERE
name = '4-Persoons Ranchtent'
AND ( c.usage_date_from BETWEEN '01-APR-20' AND '01-MAY-20'
OR c.usage_date_to BETWEEN '01-APR-20' AND '01-MAY-20' );
I want a proper plsql code using procedure or loop that updates all records of column bookdate_to.

There is no need for PL/SQL loops. We can do this in plain SQL:
update cashflowrule c
set c.bookdate_to = date '2019-09-29'
where ( c.usage_date_from BETWEEN date '2020-04-01' AND date '2020-05-01'
OR c.usage_date_to BETWEEN date '2020-04-01' AND date '2020-05-01' )
and c.bookdate_to is null -- not sure whether this is a rule, your comments confuse me
and c.cashflowmanager_id in ( select r.cashflowmanager_id
from resourcebasei18n r
where r.name = '4-Persoons Ranchtent' )

As has been pointed out this is easily accomplished with a single SQL but since you require plsql here's an anonymous block for it..
declare
cursor c_cashflowrule is
select c.bookdate_to
from cashflowrule c
where ( c.usage_date_from between date '2019-09-29' and date '2020-05-01'
or c.usage_date_to between date '2019-09-29' and date '2020-05-01'
)
and exists (select null
from resourcebasei18n r
where c.cashflowmanager_id = r.cashflowmanager_id
and r.name = '4-Persoons Ranchtent'
)
for update of bookdate_to;
begin
for cflow in c_cashflowrule
loop
update cashflowrule
set bookdate_to = date '2019-09-29'
where current of c_cashflowrule ;
end loop;
end ;

Related

Update Sql query to set end date based on start date

:-Need help in sql query for updating a table
update query-
set END_DATE of first record should be the start date of second record, for a particular id .-as highlighted in yellow(order by start date ).
Also the final record for each id ( in this case for id=1&2 , record with end date 12/31/9999 should not touch and leave as it is.
How to do that?
If you have SQL Server 2016+ you can use the LEAD function.
UPDATE MyTable
SET [END_DATE] = COALESCE([DesiredData].END_DATE, '9999-12-31')
FROM MyTable
INNER JOIN ( SELECT
[id],
[START_DATE],
LEAD([START_DATE]) OVER(PARTITION BY [id], ORDER BY [START_DATE]) [END_DATE]
FROM MyTable
) AS [DesiredData]
ON MyTable.[id] = [DesiredData].[id]
AND MyTable.[START_DATE] = [DesiredData].[START_DATE]
If you do not, you could do a correlated subquery:
UPDATE MyTable
SET [END_DATE] = COALESCE(
( SELECT TOP 1 END_DATE
FROM MyTable [NextRecord]
WHERE [NextRecord].[id] = MyTable.id
AND [NextRecord].[START_DATE] > MyTable.[START_DATE]
ORDER BY NextRecord.[START_DATE]
),
'9999-12-31'
)

Delete the records repeated by date, and keep the oldest

I have this query, and it returns the following result, I need to delete the records repeated by date, and keep the oldest, how could I do this?
select
a.EMP_ID, a.EMP_DATE,
from
EMPLOYES a
inner join
TABLE2 b on a.table2ID = b.table2ID and b.ID_TYPE = 'E'
where
a.ID = 'VJAHAJHSJHDAJHSJDH'
and year(a.DATE) = 2021
and month(a.DATE) = 1
and a.ID <> 31
order by
a.DATE;
Additionally, I would like to fill in the missing days of the month ... and put them empty if I don't have that data, can this be done?
I would appreciate if you could guide me to solve this problem
Thank you!
The other answers miss some of the requirement..
Initial step - do this once only. Make a calendar table. This will come in handy for all sorts of things over the time:
DECLARE #Year INT = '2000';
DECLARE #YearCnt INT = 50 ;
DECLARE #StartDate DATE = DATEFROMPARTS(#Year, '01','01')
DECLARE #EndDate DATE = DATEADD(DAY, -1, DATEADD(YEAR, #YearCnt, #StartDate));
;WITH Cal(n) AS
(
SELECT 0 UNION ALL SELECT n + 1 FROM Cal
WHERE n < DATEDIFF(DAY, #StartDate, #EndDate)
),
FnlDt(d, n) AS
(
SELECT DATEADD(DAY, n, #StartDate), n FROM Cal
),
FinalCte AS
(
SELECT
[D] = CONVERT(DATE,d),
[Dy] = DATEPART(DAY, d),
[Mo] = DATENAME(MONTH, d),
[Yr] = DATEPART(YEAR, d),
[DN] = DATENAME(WEEKDAY, d),
[N] = n
FROM FnlDt
)
SELECT * INTO Cal FROM finalCte
ORDER BY [Date]
OPTION (MAXRECURSION 0);
credit: mostly this site
Now we can write some simple query to stick your data (with one small addition) onto it:
--your query, minus the date bits in the WHERE, and with a ROW_NUMBER
WITH yourQuery AS(
SELECT a.emp_id, a.emp_date,
ROW_NUMBER() OVER(PARTITION BY CAST(a.emp_date AS DATE) ORDER BY a.emp_date) rn
FROM EMPLOYES a
INNER JOIN TABLE2 b on a.table2ID = b.table2ID
WHERE a.emp_id = 'VJAHAJHSJHDAJHSJDH' AND a.id <> 31 AND b.id_type = 'E'
)
--your query, left joined onto the cal table so that you get a row for every day even if there is no emp data for that day
SELECT c.d, yq.*
FROM
Cal c
LEFT JOIN yourQuery yq
ON
c.d = CAST(yq.emp_date AS DATE) AND --cut the time off
yq.rn = 1 --keep only the earliest time per day
WHERE
c.d BETWEEN '2021-01-01' AND EOMONTH('2021-01-01')
We add a rownumbering to your table, it restarts every time the date changes and counts up in order of time. We make this into a CTE (or a subquery, CTE is cleaner) then we simply left join it to the calendar table. This means that for any date you don't have data, you still have the calendar date. For any days you do have data, the rownumber rn being a condition of the join means that only the first datetime from each day is present in the results
Note: something is wonky about your question . You said you SELECT a.emp_id and your results show 'VJAHAJHSJHDAJHSJDH' is the emp id, but your where clause says a.id twice, once as a string and once as a number - this can't be right, so I've guessed at fixing it but I suspect you have translated your query into something for SO, perhaps to hide real column names.. Also your SELECT has a dangling comma that is a syntax error.
If you have translated/obscured your real query, make absolutely sure you understand any answer here when translating it back. It's very frustrating when someone is coming back and saying "hi your query doesn't work" then it turns out that they damaged it trying to translate it back to their own db, because they hid the real column names in the question..
FInally, do not use functions on table data in a where clause; it generally kills indexing. Always try and find a way of leaving table data alone. Want all of january? Do like I did, and say table.datecolumn BETWEEN firstofjan AND endofjan etc - SQLserver at least stands a chance of using an index for this, rather than calling a function on every date in the table, every time the query is run
You can use ROW_NUMBER
WITH CTE AS
(
SELECT a.EMP_ID, a.EMP_DATE,
RN = ROW_NUMBER() OVER (PARTITION BY a.EMP_ID, CAST(a.DATE as Date) ORDER BY a.DATE ASC)
from EMPLOYES a INNER JOIN TABLE2 b
on a.table2ID = b.table2ID
and b.ID_TYPE = 'E'
where a.ID = 'VJAHAJHSJHDAJHSJDH'
and year(a.DATE) = 2021
and MONTH(a.DATE) = 1
and a.ID <> 31
)
SELECT * FROM CTE
WHERE RN = 1
Try with an aggregate function MAX or MIN
create table #tmp(dt datetime, val numeric(4,2))
insert into #tmp values ('2021-01-01 10:30:35', 1)
insert into #tmp values ('2021-01-02 10:30:35', 2)
insert into #tmp values ('2021-01-02 11:30:35', 3)
insert into #tmp values ('2021-01-03 10:35:35', 4)
select * from #tmp
select tmp.*
from #tmp tmp
inner join
(select max(dt) as dt, cast(dt as date) as dt_aux from #tmp group by cast(dt as date)) compressed_rows on
tmp.dt = compressed_rows.dt
drop table #tmp
results:

How to select data without using group?

My base data based on dealer code only but in one condition we need to select other field as well to matching the condition in other temp table how can i retrieve data only based on dealercode ith matching the condition on chassis no.
Below is the sample data:
This is how we have selected the data for the requirement:
---------------lastyrRenewalpolicy------------------
IF OBJECT_ID('TEMPDB..#LASTYRETEN') IS NOT NULL DROP TABLE #LASTYRETEN
select DEALERMASTERCODE , count(*) RENEWALEXPRPOLICY,SUM(NETOD_YEAR_PREM_PART_A) AS 'ACHIEVED-ODPREMIUM_RENEWAL' into #LASTYRETEN
from [dbo].[T_RE_POLICY_TRANSACTION]
where cast (InsPolicyCreatedDate as date) between #FirstDayC and #LastDayC
AND PolicyStatus= 'Renewal' AND (ltrim(rtrim(ISCANCELLEDSTATUS)) = 0 ) group by DEALERMASTERCODE
-----------------lastrollower------------------------
IF OBJECT_ID('TEMPDB..#LASTYROLWR') IS NOT NULL DROP TABLE #LASTYROLWR
select DEALERMASTERCODE , count(*) ROLLOWEEXPRPOLICY ,SUM(NETOD_YEAR_PREM_PART_A) AS 'ACHIEVED-ODPREMIUM_ROLLOVER'
into #LASTYROLWR from [dbo].[T_RE_POLICY_TRANSACTION] where cast (InsPolicyCreatedDate as date) between #FirstDayC and #LastDayC
AND PolicyStatus= 'ROLLOVER' AND (ltrim(rtrim(ISCANCELLEDSTATUS)) = 0 ) group by DEALERMASTERCODE
And continue with above flow Below is the other select statement which creating issue at the end due to grouping
:
-------------OTHERYRBASE(EXPIRYRENEWAL)--------------
IF OBJECT_ID('TEMPDB..#OTHERYRBASEEXPIRY') IS NOT NULL DROP TABLE #OTHERYRBASEEXPIRY
select DEALERMASTERCODE ,ChassisNo , count(*) RENEWALPOLICYEXPIRY
into #OTHERYRBASEEXPIRY
from [dbo].[T_RE_POLICY_TRANSACTION] where cast (PolicyExpiryDate as date) between '2020-08-01' and '2020-08-31'
and BASIC_PREM_TOTAL <> 0 AND PolicyStatus in ('Renewal','rollover') and BusinessType='jcb'
AND (ltrim(rtrim(ISCANCELLEDSTATUS)) = 0 ) group by DEALERMASTERCODE,ChassisNo
-------------OTHERYRBASE(EXPIRYRENEWAL)--------------
IF OBJECT_ID('TEMPDB..#OTHERYRCON') IS NOT NULL DROP TABLE #OTHERYRCON
select OTE.DEALERMASTERCODE ,OTE.ChassisNo , count(*) OTHERYRCON into #OTHERYRCON
from [dbo].[T_RE_POLICY_TRANSACTION] OTE INNER JOIN #OTHERYRBASEEXPIRY EXP
ON OTE.ChassisNo=EXP.ChassisNo
where cast(CREATED_DATE as date) between '2020-06-01' and '2020-12-31' and BusinessType='jcb'
and OTE.BASIC_PREM_TOTAL <> 0 AND OTE.PolicyStatus = 'Renewal'
AND (ltrim(rtrim(ISCANCELLEDSTATUS)) = 0 ) group by OTE.DEALERMASTERCODE,OTE.ChassisNo
Thanks a lot in advance for helping and giving a solution very quickly ///
After taking a look at this code it seems possible there was an omitted JOIN condition in the last SELECT statement. In the code provided the JOIN condition is only on ChassisNo. The GROUP BY in the prior queries which populates the temporary table also included the DEALERMASTERCODE column. I'm thinking DEALERMASTERCODE should be added to the JOIN condition. Something like this
select OTE.DEALERMASTERCODE ,OTE.ChassisNo , count(*) OTHERYRCON
into #OTHERYRCON
from [dbo].[T_RE_POLICY_TRANSACTION] OTE
INNER JOIN #OTHERYRBASEEXPIRY EXP ON OTE.DEALERMASTERCODE=EXP.DEALERMASTERCODE
and OTE.ChassisNo=EXP.ChassisNo
where cast(CREATED_DATE as date) between '2020-06-01' and '2020-12-31'
and BusinessType='jcb'
and OTE.BASIC_PREM_TOTAL <> 0
AND OTE.PolicyStatus = 'Renewal'
AND (ltrim(rtrim(ISCANCELLEDSTATUS)) = 0 )
group by OTE.DEALERMASTERCODE,OTE.ChassisNo;

Displaying student absent dates

Here is the table I want to display:
tblAttendance table
CustomerId
Id
Attendence
Date
and
tblStudent
CustomerId
Name
Now I want to search by from to date and want absent date.
How can I achieve this?
I tried below code:
ALTER PROCEDURE spExceptDate
AS
declare #StartDate DATE, #EndDate DATE
set #StartDate = '2020-02-15';
set #EndDate = '2020-02-25';
BEGIN
SELECT CustomerId,FirstName+' '+LastName,Date,Attendance
FROM
[dbo].[tblAttendance] att
LEFT JOIN
[dbo].[tblStudent] st
ON att.CustomerId = st.Code
EXCEPT
SELECT CustomerId,FirstName+' '+LastName,Date,Attendance
FROM
[dbo].[tblAttendance] att
LEFT JOIN
[dbo].[tblStudent] st
ON att.CustomerId = st.Code
where att.Date>='2020-02-15' and att.Date<='2020-02-25'
END
GO
i want date for which student absend
Basically what you need is list of possible dates between From and To
DECLARE #StartDate DATE = '2020-02-15',
#EndDate DATE = '2020-02-25' ;
--Create a CTE to get all dates between from and to (you should filter holidays and weekends)
WITH SchoolCalendar (WorkingDay)
AS (SELECT #StartDate
UNION ALL
SELECT DATEADD(DAY, 1, WorkingDay)
FROM SchoolCalendar
WHERE WorkingDay< #EndDate
)
--Use the CTE to determine the Absense records
SELECT st.Code CustomerId, st.FirstName+' '+st.LastName Name,st.WorkingDay Date, COALESCE(Attendance,'A') Attendance
FROM (SELECT * from SchoolCalendar, tblStudent) st
LEFT JOIN [dbo].[tblAttendance] att ON att.Date = st.WorkingDay AND att.CustomerId = st.Code
WHERE st.WorkingDay>=#StartDate and st.WorkingDay<=#EndDate
ORDER BY st.Code, st.WorkingDay
You may need only one query if you want to fetch only absent student names from the given date range
SELECT CustomerId,FirstName+' '+LastName,Date,Attendance
FROM [dbo].[tblAttendance] att
LEFT JOIN [dbo].[tblStudent] st ON att.CustomerId = st.Code
WHERE att.Date>='2020-02-15' and att.Date<='2020-02-25' AND att.Attendance <> 'P'
^^^^^^^^^^^^^^^^^^^^

Performance issues in T-SQL function

I have a table which provides me daily inventory status. I.e Inventory Item X, on a particular date had Y amount of quantity. I have created a function to obtain the purchase price on a particular day based on the last purchases.
When I run the query on the dailyinventorystatus table it completes within 3 minute for date > 2014-01-01. However, when I add in the function as a subquery it causes huge performance issues. It has been over 1.5 hours and the query is still running.
How do I improve this?
Here is the query:
SELECT
*,
RWReports.dbo.FindPurchasePrice(InventoryKey, Date , warehouse) as SalesPurchasePrice
FROM
DailyInventoryStatus
WHERE
Warehouse IN ('NYC,', 'CHICAGO', 'CHINA', 'ATLANTA')
AND Date >= '2014-01-01'
Here is the function:
CREATE FUNCTION [dbo].[FindPurchasePrice]
(#InventoryKey varchar(8), #InDate Date , #Warehouse varchar(30))
RETURNS REAL
AS
BEGIN
DECLARE #oPurchasePrice AS REAL ;
SELECT TOP (1)
#oPurchasePrice = UnitPurchasePrice
FROM
PurchaseTransactions
WHERE
InventoryKey = #InventoryKey
AND TransactionDate <= #InDate
AND Warehouse = #Warehouse
ORDER BY
TransactionDate DESC;
IF #oPurchasePrice IS NULL
SELECT
#oPurchasePrice = mw.cost
FROM
Rentalworks.dbo.masterwh mw
JOIN
Rentalworks.dbo.warehouse w ON w.warehouseid = mw.warehouseid
AND mw.masterid = #InventoryKey
AND w.warehouse = #Warehouse;
RETURN #oPurchasePrice;
END;
GO
This is how you could possibly convert this into an inline table valued function.
CREATE FUNCTION [dbo].[FindPurchasePrice]
(
#InventoryKey varchar(8)
, #InDate Date
, #Warehouse varchar(30)
)
RETURNS TABLE
AS
RETURN
SELECT ISNULL(pt.UnitPurchasePrice ,mw.cost) AS PurchasePrice
FROM Rentalworks.dbo.masterwh mw
JOIN Rentalworks.dbo.warehouse w on w.warehouseid = mw.warehouseid
AND mw.masterid = #InventoryKey
AND w.warehouse = #Warehouse
OUTER APPLY
(
SELECT TOP (1) UnitPurchasePrice
FROM PurchaseTransactions
WHERE InventoryKey = #InventoryKey
AND TransactionDate <= #InDate
AND Warehouse=#Warehouse
ORDER BY TransactionDate DESC
) pt
I of course can't test this but it syntax checks fine.
Now to include that in your original select statement you would do something like this.
SELECT dis.*
, fp.PurchasePrice
FROM DailyInventoryStatus dis
CROSS APPLY dbo.FindPurchasePrice(dis.InventoryKey, dis.Date, dis.warehouse) fp
WHERE Warehouse IN ('NYC,', 'CHICAGO', 'CHINA', 'ATLANTA')
AND Date >= '2014-01-01'
Here's one way to re-write without the function by incorporating all the logic into a single query:
with data as (
select
dis.*, pt.TransactionDate, pt.UnitPurchasePrice,
row_number() over (
partition by dis.InventoryKey, dis.Warehouse
order by TransactionDate desc
) as TransNumber
from
DailyInventoryStatus dis left outer join
PurchaseTransactions pt
on pt.InventoryKey = dis.InventoryKey
and pt.Warehouse = dis.Warehouse
and pt.TransactionDate < dis.Date
where dis.Date >= ?
)
select
*,
coalesce(
UnitPurchasePrice,
(
select mw.cost
from
Rentalworks.dbo.masterwh mw inner join
Rentalworks.dbo.warehouse w
on w.warehouseid = mw.warehouseid
where mw.masterid = data.InventoryKey
and w.warehouse = data.Warehouse
)
) as PurchasePrice
from data
where TransNumber = 1