SQL : Date condition Updated - sql

I want to update my column depending on dates from two different table. Below is my query
UPDATE T_TRN_DEAL_DETAILS SET MATURITY_DT =
(CASE
WHEN (Select top 1 INS.INVOICE_DUE_DT as invoice_date from T_TRN_INVOICE_DETAILS IND
INNER jOIN T_TRN_INVOICE_SUMMARY INS on IND.INVOICE_ID=INS.INVOICE_ID
where IND.DEAL_ID ='1234'
order by INS.INVOICE_DUE_DT desc ) > (Select DEAL_ID,MATURITY_DT as invoice_date from T_TRN_DEAL_DETAILS WHERE DEAL_ID ='1234')
THEN (Select top 1 INS.INVOICE_DUE_DT as invoice_date from T_TRN_INVOICE_DETAILS IND
INNER jOIN T_TRN_INVOICE_SUMMARY INS on IND.INVOICE_ID=INS.INVOICE_ID
where IND.DEAL_ID ='1234'
order by INS.INVOICE_DUE_DT desc)
ELSE (Select DEAL_ID,MATURITY_DT as invoice_date from T_TRN_DEAL_DETAILS WHERE DEAL_ID ='DL18111213586')
END )
WHERE DEAL_ID ='1234'
But I am getting below error
Only one expression can be specified in the select list when the
subquery is not introduced with EXISTS
Although I am just comparing two dates.

The error is clear:
a subquery must return only one column but in your ELSE breanch you have written as follow:
(Select DEAL_ID,MATURITY_DT as invoice_date
from T_TRN_DEAL_DETAILS WHERE DEAL_ID ='DL18111213586')
So you try to return two columns. You must remove DEAL_ID column, because your MATURITY_DT is the field you want to use to update your main table.
The same error you have done in the first branch when you try to compare subqueries (>) where the second subquery returns two columns instead of one.
(Select DEAL_ID,MATURITY_DT as invoice_date
from T_TRN_DEAL_DETAILS WHERE DEAL_ID ='1234')

Related

SQL Server - How to fill in missing column values

I have set of records at day level with 2 columns:
Invoice_date
Invoice_amount
For few records, value of invoice_amount is missing.
I need to fill invoice_amount values where it is NULL using this logic:
Look for next available invoice_amount (in dates later than the blank value record date)
For records with invoice_amount still blank (invoice_amount not present for future dates), look for most previous invoice_amount (in dates before the blank value date)
Note: We have consecutive multiple days where invoice_amount is blank in the dataset:
use CROSS APPLY to find next and previous not null Invoice Amount
update p
set Invoice_Amount = coalesce(nx.Invoice_Amount, pr.Invoice_Amount)
from Problem p
outer apply -- Next non null value
(
select top 1 *
from Problem x
where x.Invoice_Amount is not null
and x.Invoice_Date > p.Invoice_Date
order by Invoice_Date
) nx
outer apply -- Prev non null value
(
select top 1 *
from Problem x
where x.Invoice_Amount is not null
and x.Invoice_Date < p.Invoice_Date
order by Invoice_Date desc
) pr
where p.Invoice_Amount is null
this updates back your table. If you need a select query, it can be modify to it easily
Not efficient but seems to work. Try:
update test set invoice_amount =
coalesce ((select top 1 next.invoice_amount from test next
where next.invoiceDate > test.invoiceDate and next.invoice_amount is not null
order by next.invoiceDate),
(select top 1 prev.invoice_amount from test prev
where prev.invoiceDate < test.invoiceDate and prev.invoice_amount is not null
order by prev.invoiceDate desc))
where invoice_amount is null;
As per given example you could use window function with self join
update t set t.amount = tt.NewAmount
from table t
inner join (
select Dates, coalesce(min(amount) over (order by dates desc ROWS BETWEEN 1 PRECEDING AND CURRENT ROW),
min(amount) over (order by dates asc ROWS BETWEEN 1 PRECEDING AND CURRENT ROW)) NewAmount
from table t
) tt on tt.dates = t.dates
where t.amount is null

Grouping records on consecutive dates

If I have following table in Postgres:
order_dtls
Order_id Order_date Customer_name
-------------------------------------
1 11/09/17 Xyz
2 15/09/17 Lmn
3 12/09/17 Xyz
4 18/09/17 Abc
5 15/09/17 Xyz
6 25/09/17 Lmn
7 19/09/17 Abc
I want to retrieve such customer who has placed orders on 2 consecutive days.
In above case Xyz and Abc customers should be returned by query as result.
There are many ways to do this. Use an EXISTS semi-join followed by DISTINCT or GROUP BY, should be among the fastest.
Postgres syntax:
SELECT DISTINCT customer_name
FROM order_dtls o
WHERE EXISTS (
SELEST 1 FROM order_dtls
WHERE customer_name = o.customer_name
AND order_date = o.order_date + 1 -- simple syntax for data type "date" in Postgres!
);
If the table is big, be sure to have an index on (customer_name, order_date) to make it fast - index items in this order.
To clarify, since Oto happened to post almost the same solution a bit faster:
DISTINCT is an SQL construct, a syntax element, not a function. Do not use parentheses like DISTINCT (customer_name). Would be short for DISTINCT ROW(customer_name) - a row constructor unrelated to DISTINCT - and just noise for the simple case with a single expression, because Postgres removes the pointless row wrapper for a single element automatically. But if you wrap more than one expression like that, you get an actual row type - an anonymous record actually, since no row type is given. Most certainly not what you want.
What is a row constructor used for?
Also, don't confuse DISTINCT with DISTINCT ON (expr, ...). See:
Select first row in each GROUP BY group?
Try something like...
SELECT `order_dtls`.*
FROM `order_dtls`
INNER JOIN `order_dtls` AS mirror
ON `order_dtls`.`Order_id` <> `mirror`.`Order_id`
AND `order_dtls`.`Customer_name` = `mirror`.`Customer_name`
AND DATEDIFF(`order_dtls`.`Order_date`, `mirror`.`Order_date`) = 1
The way I would think of it doing it would be to join the table the date part with itselft on the next date and joining it with the Customer_name too.
This way you can ensure that the same customer_name done an order on 2 consecutive days.
For MySQL:
SELECT distinct *
FROM order_dtls t1
INNER JOIN order_dtls t2 on
t1.Order_date = DATE_ADD(t2.Order_date, INTERVAL 1 DAY) and
t1.Customer_name = t2.Customer_name
The result you should also select it with the Distinct keyword to ensure the same customer is not displayed more than 1 time.
For postgresql:
select distinct(Customer_name) from your_table
where exists
(select 1 from your_table t1
where
Customer_name = your_table.Customer_name and Order_date = your_table.Order_date+1 )
Same for MySQL, just instead of your_table.Order_date+1 use: DATE_ADD(your_table.Order_date , INTERVAL 1 DAY)
This should work:
SELECT A.customer_name
FROM order_dtls A
INNER JOIN (SELECT customer_name, order_date FROM order_dtls) as B
ON(A.customer_name = B.customer_name and Datediff(B.Order_date, A.Order_date) =1)
group by A.customer_name

Select records where the only exist 1 in a joined table

I have the following query:
SELECT
A.POSTCARD_ID, A.STAMP_ID, B.END_DT
FROM
PST_VS_STAMP A
JOIN
STAMP B ON A.POSTCARD_ID = B.POSTCARD_ID
WHERE
B.ACCOUNT LIKE 'AA%'
AND B.END_DT = '9999-12-31'
GROUP BY
A.POSTCARD_ID, A.STAMP_ID, B.END_DT
HAVING
COUNT(A.POSTCARD_ID) < 2
But I get the wrong results.
I want only the postcards ID's where there is 1 record (HAVING < 2) in the PST_VS_STAMP table. How can I query this?
Do the aggregation in the subquery, only on the table where you want one row. Because there is one row, you can use an aggregation function to pull out the value of any column (for one row min(col) is the column's value):
select s.postcard_id, vs.stamp_id, s.end_dt
from stamp s join
(select vs.postcard_id, min(stamp_id) as stamp_id
from pst_vs_stamp vs
group by vs.postcard_id
having count(*) = 1
) s
on vs.POSTCARD_ID = s.POSTCARD_ID
where s.ACCOUNT like 'AA%' and s.END_DT = '9999-12-31';

SQL: Checking whether dates are present in both tables

I have two tables
RejectionDate:
'2016-07-01'
'2016-08-01'
'2016-09-01'
PayDate:
PayDateStart PayDateEnd
'2016-08-01' '2016-09-01'
I need to check whether all dates from the first table RejectionDate fall into periods stored in the other table PayDate.
Here is a way you can get a flag per row -- which I am guessing is the real intention of the question. Regardless of the database you are using:
select r.*,
(case when exists (select 1
from paydates pd
where r.rejectiondate between p.PayDateStart and p.PayDateEnd
)
then 1 else 0
end) as InRangeFlag
from rejections r;
Join them?
select rd.*
from RejectionDate rd
inner join PayDate pd
on rd.RejectionDate between pd.PayDateStart and pd.PayDateEnd
You can use a query like the following:
SELECT COUNT(*) AS all_dates, COUNT(t2.PayDateEnd) AS all_between_dates
FROM Rejections AS t1
LEFT JOIN PayDate AS t2 ON t1.RejectionDate BETWEEN t2.PayDateStart AND t2.PayDateEnd
The first COUNT returns the number of all records of the first table, whereas the second COUNT returns the number of all records of the first table that have a date that is between start/end date of the second table.
Demo here

How to pass value to and join correlated query having WITH clause?

I need to pass from one select value to another one to get the calculation done and join the results but having problem with this "with" clause sub query.
This one calculates balance from supplied number loan_id from the second query
WITH my_select (balance, type) As
(
select sum(amount_o-amount_h) as balance, type
from decret d
where d.idnumber = **loan_id**
and d.DATE_D <= '2013-10-31'
and type in (1,2,3,4,11,12,13,18,20,25)
group by type
having sum(amount_o-amount_h) <> 0
)
select sum(balance) FROM my_select //just returns client balance
And this one selects clients data and I want for every client add the balance also to the result calculated in first query.
SELECT *, /*balance*/ from clients
where **loan_id** in (select LoanNum
from NumsFromEx)
How to join them together? (I have simplified a little bit queries to show it cleaner)
Given the sample queries, the following should work:
WITH cteBalances AS
(
SELECT loan_id, SUM(amount_o-amount_h) AS balance
FROM decret d
WHERE d.DATE_D <= '2013-10-31'
AND type IN (1,2,3,4,11,12,13,18,20,25)
GROUP BY loan_id
)
SELECT c.*, b.balance
FROM clients c
LEFT JOIN cteBalances b ON b.loan_id = c.loan_id
WHERE loan_id IN (SELECT LoanNum
FROM NumsFromEx)