Multiple Insert, Update and Delete [closed] - sql

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I am having three tables
Present , History and Hold Table
By the end of the day I have to clear the entire present table data like
If there is an Id with the existing one in History table then I have to look at the balance and then subtract current balance from that table.
If balance- currentbalance < 0 then subtract the balance from history table make it zero and add a new row in history table with currentbalance-balance as balance
If there is no ID in history table and currentbalance > 0 move that record to hold table .
If currentbalance < = 0 change the date in the present table to next day. I just need a single query for all this.
If there is a record in hold table remove that record and place the latest record.
Every time when u update a row change the DATE column to current date apart from present table.
Present:
ID Current_Balance Date
1 2000 25-06-2014
2 1500 25-06-2014
3 5000 25-06-2014
2 6000 25-06-2014
4 -200 25-06-2014
History:
ID Balance Date
1 1500 24-06-2014
2 1600 24-06-2014
Hold Table:
ID Balance Date
3 125 24-06-2014
4 2000 24-06-2014
I want result like
Present:
ID Current_Balance Date
4 -200 26-06-2014
History:
Id Balance Date
1 0 25-06-2014
1 500 25-06-2014
2 100 25-06-2014
Hold:
ID Balance Date
3 5000 25-06-2014
4 2000 24-06-2014
I have written my code which is a bit complex. Can anyone give me optimum solution for this.

Use following query:
UPDATE Persent
SET current_Balance = 0,
Date = GETDATE()
OUTPUT Inserted.ID, Deleted.current_Balance, Deleted.Date
INTO #P
FROM History h
WHERE h.Id = Persent.ID
AND h.balance - Persent.current_balance < 0
INSERT INTO History (Id, Balance, Date)
SELECT p.Id, p.Current_Balance, p.Date
FROM #P p
MERGE Hold AS Destination
USING (SELECT * FROM Present WHERE Current_Balance>0) AS Source ON Destination.Id = Source.Id
WHEN NOT MATCHED THEN INSERT (Id, Balance, Date) VALUES (ID,Current_Balance,Date)
WHEN MATCHED THEN DELETE
UPDATE Persent
SET Date = DATEADD(DAY, 1, GETDATE())
WHERE Persent.current_balance <= 0

Related

How to use group by with case statement [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 11 months ago.
Improve this question
I have a table with following fields
CREATE TABLE Tblstock
( ID int , SlNo int, Storage varchar(10), stock int);
insert into Tblstock values
(1, 1, 'STORE', 100),
(2, 1, 'Floor 1', 20),
(3, 2, 'STORE', 2000),
(4, 2, 'Floor 1', 40);
I have to dynamically update the left over quantity in store after it got consumed on floor1, I have written a code to calculate qty in store using below mentioned query,
SELECT (
(SELECT CASE WHEN COUNT(B.SlNo) > 1 OR B.Storage = 'STORE' THEN SUM(B.Stock)END FROM TblStock B GROUP BY B.SlNo) -
(SELECT CASE WHEN COUNT(B.SlNo) > 1 OR B.Storage <> 'STORE' THEN SUM(B.Stock)END FROM TblStock B GROUP BY B.SlNo))
However it is not generating the desired result and throwing error
Can anybody help to write it properly so that I get single value of remaining quantity in store
You just need a straight-forward grouping and conditional aggregation
SELECT
s.SlNo,
Total = SUM(CASE WHEN s.Storage = 'STORE' THEN s.Qty ELSE -s.Qty END)
FROM TblStock s
GROUP BY
s.SlNo;
db<>fiddle
Assuming what you are trying to do is to deduct the quantity (qty) in storage called store by the sum of the rest of the other storage. I could think of a query like this:
select *,
(Qty - (select sum(b.Qty) from tblstock as b
where b.Storage <> 'store'
and b.SINo = a.SINo
group by b.SINo)) as remainingQty
from tblstock as a
where a.Storage = 'store' group by a.SINo
The query above, with the following input:
ID
SINo
Storage
Qty
1
1
store
100
2
1
floor 1
20
3
1
floor 2
30
4
2
store
100
5
2
floor 1
40
6
2
floor 2
50
It produces the following output:
ID
SINo
Storage
Qty
remainingQty
1
1
store
100
50
4
2
store
100
10
You can find the SQLFiddle here.
Note:
If you are want to avoid subquery and have the urge to chug in join fiddle:
select a.id,
a.SINo,
a.Storage,
a.Qty,
c.Qty,
(a.Qty - c.Qty) as remainingQty
from tblstock as a
join
(select b.SINo,
sum(b.Qty) as Qty
from tblstock as b
where b.Storage <> 'store'
group by b.SINo) as c
on c.SINo = a.SINo
where a.Storage = 'store' group by a.SINo

Is there SQL Logic to reduce type 2 table along a dimension

I have a slowly changing type 2 price change table which I need to reduce the size of to improve performance. Often rows are written to the table even if no price change occurred (when some other dimensional field changed) and the result is that for any product the table could be 3-10x the size it needs to be if it were including only changes in price.
I'd like to compress the table so that it only has contains the first effective date and last expiration date for each price until that price changes that can also
Deal with an unknown number of rows of the same price
Deal with products going back to an old price
As an example if i have this raw data:
Product
Price Effective Date
Price Expiration Date
Price
123456
6/22/18
9/19/18
120
123456
9/20/18
11/8/18
120
123456
11/9/18
11/29/18
120
123456
11/30/18
12/6/18
120
123456
12/7/18
12/19/18
85
123456
12/20/18
1/1/19
85
123456
1/2/19
2/19/19
85
123456
2/20/19
2/20/19
120
123456
2/21/19
3/19/19
85
123456
3/20/19
5/22/19
85
123456
5/23/19
10/10/19
85
123456
10/11/19
6/19/19
80
123456
6/20/20
12/31/99
80
I need to transform it into this:
Product
Price Effective Date
Price Expiration Date
Price
123456
6/22/18
12/6/18
120
123456
12/7/18
2/19/19
85
123456
2/20/19
2/20/19
120
123456
2/21/19
10/10/19
85
123456
10/11/19
12/31/99
80
You can first find the intervals where the price does not change, and then group on those intervals:
with to_r as (select row_number() over (order by (select 1)) r, t.* from data_table t),
to_group as (select t.*, (select sum(t1.r < t.r and t1.price != t.price) from to_r t1) c from to_r t)
select t.product, min(t.effective), max(t.expiration), max(t.price) from to_group t group by t.c order by t.r;
Output:
Product
Price Effective Date
Price Expiration Date
Price
123456
6/22/18
12/6/18
120
123456
12/7/18
2/19/19
85
123456
2/20/19
2/20/19
120
123456
2/21/19
10/10/19
85
123456
10/11/19
12/31/99
80
This is a type of gaps-and-islands problem. I would recommend reconstructing the data, saving it in a temporary table, and then reloading the existing table.
The code to reconstruct the data is:
select product, price, min(effective_date), max(expiration_date)
from (select t.*,
sum(case when prev_expiration_date = effective_date - interval '1 day' then 0 else 1 end) over (partition by product order by effective_date) as grp
from (select t.*,
lag(expiration_date) over (partition by product, price order by effective_date) as prev_expiration_date
from t
) t
) t
group by product, price, grp;
Note that the logic for date arithmetic varies depending on the database.
Save this result into a temporary table, temp_t or whatever, using select into, create table as, or whatever your database supports.
Then empty the current table and reload it:
truncate table t;
insert into t
select product, price, effective_date, expiration_date
from temp_t;
Notes:
Validate the data before using truncate_table!
If there are triggers or columns with default values, you might want to be careful.
It sounds like you are asking for a temporal schema? Where for a given date you can know the price of an asset?
This is done with two tables; price_current and price_history.
price_id
item_id
price
rec_created
1
1
100
'2015-04-18'
price_id
item_id
from
to
price
1
1
'2001-01-01'
'2004-05-01'
114
1
1
'2004-05-01'
'2015-04-18'
102
i.e. for any item, you can ascertain the date it was set without polluting your "current" table. For this to work effectively you will need to have UPDATE triggers on your current_table. When you update a record you insert into the history table the details and the period it was valid from.
CREATE OR REPLACE TRIGGER trg_price_current_update
AS
BEGIN
INSERT INTO price_history(price_id, item_id, from, to, price)
SELECT price_id, item_id, rec_created, GETDATE(), price
FROM rows_updated
END
Now you have a distinction between current and historical, without your current table (presumably the busier table) getting out of hand because of maintaining historical state. Hope i understood the question.
To ignore 'dummy' updates, just alter the trigger to ignore empty changes (if that's not handled by the DBMS anyway). Tbh, this should and could be done application side easily enough, but to manage it via the trigger:
CREATE OR REPLACE TRIGGER trg_price_current_update
AS
BEGIN
INSERT INTO price_history(price_id, item_id, from, to, price)
SELECT price_id, item_id, rec_created, GETDATE(), price
FROM rows_updated u
INNER JOIN price_current ON u.price_id = p.price_id
WHERE u.price <> p.price
END
i.e. rows_updated contains the record from the update, we insert into the history table the previous row, providing the previous row's price is different from the current row's price.
(edited to include new trigger. I also changed the date held in rec_created, this must be the date the row is created, not the first instance that product had a price assigned to it. that was a mistake. Regarding the dates, I am lazy to put the full DD-MM-YYYY hh:mm:ss:zzz, but that would generally be useful in between queries)
What you are asking for is a versioning system. Many RDBMS platforms implement support for this out of the box (it's a SQL standard), which may be suitable, depending on your requirements.
You have not tagged a specific platform so it's not possible to be specific to your situation. I use the concept of system versioning regularly in MS Sql Server, where you would implement it thus:
assuming schema "history" exists,
alter table dbo.MyTable add
ValidFrom datetime2 generated always as row start hidden constraint DF_MyTableSysStart default sysutcdatetime(),
ValidTo datetime2 generated always as row end hidden constraint DF_MyTableSysEnd default convert(datetime2, '9999-12-31 23:59:59.9999999'),
period for system_time (ValidFrom, ValidTo);
end
alter table MyTable set (system_versioning = on (history_table = History.MyTable));
create clustered index ix_MyTable on History.MyTable (ValidTo, ValidFrom) with (data_compression = page,drop_existing=on) on History;
A number of syntax extensions exist to aid querying the temporal data for example to find historical data at a point in time.
Alternatively, to utilise a single table but handle the duplication, you could create an instead of trigger.
the idea here is that the trigger gets to intercept the data before it is inserted, where you can check to see of the value is different to the last value and discard or insert as appropriate.
something along the lines of:
WITH keeps AS
(
SELECT p.product_id, p.effective, p.expires, p.price, CASE WHEN EXISTS(SELECT 1 FROM prices p1 WHERE p1.effective = DATEADD(DAY, p.exires, 1) AND p1.price <> p.price) THEN 1 ELSE 0 END AS has_after, CASE WHEN EXISTS(SELECT 1 FROM prices p1 WHERE p1.expires = DATEADD(DAY, p.effective, -1) AND p1.price <> p.price) THEN 1 ELSE 0 END AS has_before
FROM prices p
)
SELECT * FROM keeps
WHERE has_after = 1
OR has_before = 1
UNION ALL
SELECT p.product_id, p.effective, p.exires, p.price
FROM prices p
WHERE p.effective = (SELECT MIN(effective) FROM prices p1 WHERE p1.product_id = p.product_id)
What's it doing:
Find all the entries where there exists another entry whose effective date is that of the previous entry's expiry date + 1, and the price of that new entry is different. This gives us all the actual changes in price. But we miss the first price entry, so we simply include that in the results.
e.g.:
product_id
effective
expires
price
has_before
has_after
123456
6/22/18
9/19/18
120
0
0
123456
9/20/18
11/8/18
120
0
0
123456
11/9/18
11/29/18
120
0
0
123456
11/30/18
12/6/18
120
0
1
123456
12/7/18
12/19/18
85
1
0
123456
12/20/18
1/1/19
85
0
0
123456
2/1/19
2/19/19
85
0
1
123456
2/20/19
2/20/19
120
1
1
123456
2/21/19
3/19/19
85
1
0

Create CASE WHEN labels based on DISTINCT values in a particular column [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I have data of the following form:
ID
Category
Amount
1
A
100
1
B
200
1
B
150
1
C
500
2
B
20
3
A
100
1
B
100
I wish to GROUP BY the column ID, find out the DISTINCT types of Category present for each ID group and create a new column where I can create the following classification labels for each grouped ID based on the unique or distinct categories present and also calculate the corresponding sum of amount for each grouped ID. So the output should be the follows:
ID
Classification
Sum of Amount
1
ALL
950
1
B only
20
1
A and B only
200
I tried the following SQL code but it doesn't work, most likely because DISTINCT() command within a CASE WHEN statement cannot consider multiple values.
My query:
SELECT
ID,
(CASE WHEN DISTINCT(CATEGORY) IN ("A") then "A Only" WHEN WHEN DISTINCT(CATEGORY) IN ("B") THEN "B only"..........)
SUM(AMOUNT)
FROM Table
GROUP BY 1,2
I have tried multiple ways of using the DISTINCT statement with CASE WHEN but none of them works.
Hmmm . . . How about this?
select id,
(case when min(category) = max(category)
then min(category) || ' only'
when count(distinct category) = 3
then 'All'
when min(category) is NULL
then 'None'
else min(category) || ' and ' || max(category)
end)
from t
group by id;

Update only one row of duplicate rows in SQL [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
i have a problem. I have tried to search other questions but not working for my problem, so i try to explain it.
I have a table that has 7 columns with 4 primary keys:
Istituto, Filiale, Cdg, Progressivo, DataInserimento, Operatore, Testo
First 4 column are keys and there are sometime record with simil keys (same Istituto, Cdg and Progressivo) but same DataInserimento (that is a DATETIME field), i would found this record and change one (or more if there are more than two record with same date and Cdg) adding 100 ms for distinct date from each other.
For example:
Istituto Filiale cdg Progressivo DataInserimento Operatore Testo
1 12 456 1 12/11/2015 12:00:00:200 UGO QUALCOSA
1 123 456 1 12/11/2015 12:00:00:200 UGO QUALCOSA2
1 124 456 1 12/11/2015 12:00:00:200 UGO QUALCOSA3
I Would like this after query:
Istituto Filiale cdg Progressivo DataInserimento Operatore Testo
1 12 456 1 12/11/2015 12:00:00:200 UGO QUALCOSA
1 123 456 1 12/11/2015 12:00:00:300 UGO QUALCOSA2
1 124 456 1 12/11/2015 12:00:00:400 UGO QUALCOSA3
Can I solve this problem?
In this UPDATE you change dataInserimento adding a multiple of 100 ms in base of previous rows of your row if exists another row with the same 3 fields of primary key (except Filiale)
Try this:
UPDATE yourtable
SET dataInserimento = DATEADD(ms, 100 *
(SELECT COUNT(*)
FROM yourtable T3
WHERE T3.Istituto = yourtable.Istituto
AND T3.cdg = yourtable.cdg
AND T3.Progressivo = yourTable.Progressivo
AND T3.filiale < yourTable.filiale)
, dataInserimento)
WHERE EXISTS(
SELECT 'next'
FROM yourtable t2
WHERE T2.Istituto = yourtable.Istituto
AND T2.cdg = yourtable.cdg
AND T2.Progressivo = yourTable.Progressivo
AND T2.filiale < yourTable.filiale
)
You can do this:
UPDATE ToUpdate SET DataInserimento = SQ.NewDate
FROM TheTable ToUpdate
JOIN (
SELECT T.Istituto, T.Cdg, T.Progressivo, T.Filiale, DATEADD(ms, ROW_NUMBER() OVER (PARTITION BY T.Istituto, T.Cdg, T.Progressivo, T.DataInserimento ORDER BY T.Filiale) * 100, T.DataInserimento) AS NewDate
FROM (
SELECT Istituto, Cdg, Progressivo, DataInserimento, MIN(Filiale) AS MINFiliale
FROM TheTable
GROUP BY Istituto, Cdg, Progressivo, DataInserimento
HAVING COUNT(*) > 0
) AS Groups
JOIN TheTable T ON Groups.Istituto = T.Istituto AND Groups.Cdg = T.Cdg AND Groups.Progressivo = T.Progressivo AND Groups.DataInserimento = T.DataInserimento
WHERE T.Filiale <> MINFiliale
) SQ ON SQ.Istituto = ToUpdate.Istituto AND SQ.Cdg = ToUpdate.Cdg AND SQ.Progressivo = ToUpdate.Progressivo AND SQ.Filiale = ToUpdate.Filiale

Select rows that if I sum their value = 0 from table [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
Let's say I have a table like this:
ID. Location. Value.
1. AGF. 10.00
2. VHJ. -20.00
3. AGF. -20.00
4. AGF. 5.00
5. KLZ. 50.00
6. AGF. 10.00
I want to select the rows that have same Location AND whose Values sum to zero.
In this case the result should be:
1
3
6
because those rows are all in Location AGF and they sum to 0 (10 + -20 + 10).
Try:
Select ID from YourTable where Location IN(
Select location from YouTable
Group By Location
Having sum(Value) = 0
)
You need to find all locations with zero sum using grouping and group filters (group by and having clauses respectively). This can be done in a subquery. Then select all IDs with the just selected locations.
select ID
from YOUR_TABLE
where Location in (
select Location
from YOUR_TABLE
group by Location
having sum(Value) = 0
)
You could use GROUP BY and HAVING, like this:
Select ID from tablelocation where Location IN(
Select location from tablelocation
Group By Location
Having sum(Value) = 0
)