update a field based on subtotal from another table - sql

I'm using oracle(10).
I've got two tables as follows:
Table1 (uniq rows):
ID AMOUNT DATE
Table2:
ID AMOUNT1 AMOUNT2 ...AMOUNTN DATE
Table2 is connected many to one to Table1 connected via ID.
What I need is update-ing Table1.DATE with: the last (earliest) date from Table2 where Table1.AMOUNT - SUM(Table2.AMOUNT1) <= 0, when reading table 2 backwards by the Table2.DATE field.
Is there a simple way to do it?
Thanks in advance!
UPDATE: as I see from your answers I had misspecified the question a bit. So here goes a detailed example:
Table1 has:
ID: 1 AMOUNT:100 DATE:NULL
Table2 has (for ID: 1 so ID is not listed in here):
AMOUNT1 DATE
50 20080131
30 20080121
25 20080111
20 20080101
So in this case I need 20080111 as the DATE in Table1 as 50+30+25 => 100.

Based on your revised question, this is a case for using analytic functions.
Assuming you meant >=100 rather than <= 100 as your example implies, and renaming columns DATE to THEDATE since DATE is a reserved word in Oracle:
update table1 set thedate=
( select max(thedate) from
( select id, thedate,
sum(amount1) over (partition by id order by thedate desc) cumsum
from table2
) v
where v.cumsum >= 100
and v.id = table1.id
)
If the 100 means the current value of table1 then change that line to:
where v.cumsum >= table1.amount

First off - your database layout feels severely wrong, but I guess you can't / don't want to change it. Table1 should probably be a view, and Table2 does not make the impression of proper normalization. Something like (ID, AMOUNT_TYPE, AMOUNT_VALUE, DATE) would make much more sense to me.
But to solve your problem (this is T-SQL "UPDATE FROM" syntax, but I think Oracle knows it):
UPDATE
Table1
SET
Date = Table2Aggregate.MinDate
FROM
Table1
INNER JOIN (
SELECT Id, SUM(Amount1) SumAmount1, MIN(Date) MinDate
FROM Table2
GROUP BY Id
) AS Table2Aggregate ON Table1.Id = Table2Aggregate.ID
WHERE
Table1.Amount - Table2Aggregate.SumAmount1 <= 0

Related

Need help in sql Query.neo

In this table there are three colum and in need the value for of data which are lesser than code = 28,this is my query
SELECT value,code,date
FROM table
order by date,vchcode
but when i ad where clouse like
SELECT value,code,date
FROM table
where code < 28
order by date,vchcode
is only shows 2 row with code 26 and 27... i need 26,27 and 32.. and table colums are variable its not fix..
I think you wnat to take the date into account -- what you really want are all rows before the date of the row with code 28.
One method uses a subquery:
SELECT t.value, t.code, t.date
FROM table t
WHERE date < (SELECT date FROM table t2 WHERE t2.code = 28)
ORDER BY t.date, t.vchcode

Update record for the last week

I'm building a report that needs to show how many users were upgraded from account status 1 to account status 2 each hour for the last week (and delete hours where the upgrades = 0). My table has an updated date, however it isn't certain that the account status is the item being updated (it could be contact information etc).
The basic table config that I'm working with is below. There are other columns but they aren't needed for my query.
account_id, account_status, updated_date.
My initial idea was to first filter and look at the data for the current week, then find if they were at account_status = 1 and later account_status = 2.
What's the best way to tackle this?
This is the kind of thing that you would use a SELF JOIN for. It's tough to say exactly how to do this without getting any kind of example data, but hopefully you can build off of this at least. There are a lot of tutorials on how to write a successful self join, so I'd refer to those if you're having difficulties.
select a.account_id
from tableName a, tableName b
where a.account_id= b.account_id
and
(a.DateModified > 'YYYY-MM-DD' and a.account_status = 1)
and
(b.DateModified < 'YYYY-MM-DD' and b.account_status= 2)
Maybe you could try to rank all the updates older than an update, with a status of 2 for an account by the timestamp descending. Check if such an entry with status 1 and rank 1 exists, to know that the respective younger update did change the status from 1 to 2.
SELECT *
FROM elbat t1
WHERE t1.account_status = 2
AND EXISTS (SELECT *
FROM (SELECT rank() OVER (ORDER BY t2.updated_date DESC) r,
t2.account_status
FROM elbat t2
WHERE t2.account_id = t1.account_id
AND t2.updated_date <= t1.updated_date) x
WHERE x.account_status = 1
AND x.r = 1);
Then, to get the hours you, could create a table variable and fill it with the hours worth a week (unless you already have a suitable calender/time table). Then INNER JOIN that table (variable) to the result from above. Since it's an INNER JOIN hours where no status update exists won't be in the result.
DECLARE #current_time datetime = getdate();
DECLARE #current_hour datetime = dateadd(hour,
datepart(hour,
#current_time),
convert(datetime,
convert(date,
#current_time)));
DECLARE #hours
TABLE (hour datetime);
DECLARE #interval_size integer = 7 * 24;
WHILE #interval_size > 0
BEGIN
INSERT INTO #hours
(hour)
VALUES (dateadd(hour,
-1 * #interval_size,
#current_hour));
SET #interval_size = #interval_size - 1;
END;
SELECT *
FROM #hours h
INNER JOIN (SELECT *
FROM elbat t1
WHERE t1.account_status = 2
AND EXISTS (SELECT *
FROM (SELECT rank() OVER (ORDER BY t2.updated_date DESC) r,
t2.account_status
FROM elbat t2
WHERE t2.account_id = t1.account_id
AND t2.updated_date <= t1.updated_date) x
WHERE x.account_status = 1
AND x.r = 1)) y
ON convert(date,
y.updated_date) = h.convert(date,
h.hour)
AND datepart(hour,
y.updated_date) = datepart(hour,
h.hour);
If you use this often and/or performance is important, you might consider to introduce persistent, computed and indexed columns for the convert(...) and datepart(...) expressions and use them in the query instead. Indexing the calender/time table and the columns used in the subqueries is also worth a consideration.
(Disclaimer: Since you didn't provide DDL of the table nor any sample data this is totally untested.)

find rows with specific condition on the columns

I'm trying to make an sql query where I will compare all rows to each other and check their columns. using condition I will display the need rows.
I made this one and I got an error message "UNKNOWN COLUMN" , any ideas how to fix it?
select * from table1 as tb1 where DATE_FORMAT(start_datetime, '%H:%i:%s') in (
select DATE_FORMAT(start_datetime, '%H:%i:%s') from table1 as tb2
group by DATE_FORMAT(start_datetime, '%H:%i:%s') having count(*) > 2 AND (end_datetime = start_datetime) OR (tb1.code = tb2.code)
) AND user_id = 1
to explain what I wanna do,I'm searching the rows that have the same time then if the start_datetime is equal to the end_datetime or the rows have the same code
in this
example my sql query should return the last 4 rows, 2 because they have the same start_datetime and end_datetime and 2 because the have the same time on the start_datetime , the same code and the end_datetime is NULL
you can use "union all" operator between 2 data sets
select code,start_datetime,end_datetime from table2
where start_datetime=end_datetime
union all
select t2.code,t2.start_datetime,t2.end_datetime from
(
select code,start_datetime,end_datetime from table2
where start_datetime!=end_datetime
and end_datetime is null
) as t2
inner join ( select code table2 group by DATE_FORMAT(start_datetime, '%H:%i:%s'),code having count(*)>=2 )as t1 on t2.code=t1.code
Notwithstanding any errors we can't see because you haven't provided the schema, "fs1.code" and "fs2.code" do not seem to reference table aliases that have been defined in the statement. The aliases that do get defined are "tb1" and "tb2" and they don't match.

Select left 10 numbers, left join for a price from second table, and then sum, SQL

I am currently working in sql 2012 visual management studio. I have two tables. Table1 has three columns (ItemNumber as varchar, Quantity as int, and TimeOrdered as datetime). Table2 has 2 columns (ItemNumber as varchar, and Price as float). Please note these item numbers are not the same, the part numbers on table 1 have a letter after the number while the table 2 item number does not. For example on table 1 the item number will look something like this 999999999-E and the other table will just be 999999999-. Therefore I must use a select Left for 10 digits to get the part number.
I need to pull a list of item numbers from table 1 based on the time ordered and then cross compare that list to table 2 and multiple the price times the quantity for a grand total. Here is my code so far:
SELECT sum(tbl.quantity * table2.price) as grandtotal,
tbl.PartNumber,
tbl.quanity,
table2.price
FROM
(SELECT left(itemnumber, 10) as itemnumber, quantity
FROM table1
WHERE TimeOrdered between
('2014-05-05 00:00:00.000')
AND
('2015-05-05 00:00:00.000')) as tbl
Left table2 on
tbl.partnumber =tbl2.itemnumber
I am receiving an error here for aggregate columns but I am not sure this is the correct way to go about this to begin with.
-------------update---------------
I got it working. Sorry for taking so long to get back to you guys, I was stuck in a meeting all day,
How About This. The case is just to avoid div by Zero errors.
SELECT sum( Isnull(tbl.quantity,0) * Isnull(table2.price,0) ) as grandtotal,
tbl.PartNumber,
Sum(tbl.quanity),
case when Isnull(Sum(tbl.quanity),0) = 0 then null else
sum(Isnull(tbl.quantity,0) * Isnull(table2.price,0) ) / Sum(tbl.quanity) end
as Price
FROM
(SELECT left(itemnumber, 10) as itemnumber, quantity FROM table1 WHERE TimeOrdered between
('2014-05-05 00:00:00.000')
AND ('2015-05-05 00:00:00.000')) as tbl
Left outer join table2 on
tbl.partnumber =tbl2.itemnumber
group by tbl.PartNumber
SQL server requires you to explicitly group by the columns you're not aggregating by. So you need to add group by tbl.PartNumber, tbl.quantity, table2.price. Of course, that's probably going to make the tbl.quantity * table2.price kind of useless. What are you actually trying to do? :)
Here is a fiddle with some sample data that should give you what you want. You need to include any non-aggregate columns in your group by.
Your code ends up as follows:
SELECT left(table1.ItemNumber, 10) as PartNumber,
table2.price,
sum(table1.quantity) as totalquantity,
sum(table1.quantity * table2.price) as grandtotal
FROM table1
INNER JOIN table2 ON left(table1.ItemNumber, 10) = table2.ItemNumber
WHERE t1.Timerordered BETWEEN '2014-05-05 00:00:00.000' AND '2015-05-05 00:00:00.000'
GROUP BY table1.ItemNumber, table2.price
SQL Fiddle Example
SELECT SUM(t1.quantity * t2.price) AS 'GrandTotal'
,SUM(t1.quantity) AS 'Quantity'
,t1.itemnumber
,t2.price
FROM Table1 t1
JOIN Table2 t2 ON LEFT(t1.itemnumber, 10) = t2.itemnumber
WHERE t1.Timeordered BETWEEN '2014-05-05 00:00:00.000' AND '2015-05-05 00:00:00.000'
GROUP BY t1.itemnumber, t2.price

sql query to with insert select

I have a table with the below format:
ID curr Date Bid Ask
1 AUD/NZD 20090501 00:00:00.833 1.2866 1.28733
2 AUD/NZD 20090501 01:01:01.582 1.28667 1.2874
3 AUD/NZD 20090501 02:01:01.582 1.28667 1.28747
Now I need to select the change of Bid and Ask column and store into a different table...The result should be like the following
Bid Change Ask Change
0.0000700 0.0000700
0.0000000 0.0000700
select
t1.id,
t1.curr,
t1.date,
t1.bid,
t1.bid - t2.bid [Bid Change],
t1.ask,
t1.ask - t2.ask [Ask Change]
from tbltest t1
left join tbltest t2 on t1.ID = t2.ID + 1
order by date
This query returns everything correct except the format of Bid Change and Ask Change...like the following
BID Change ASK CHANGE
7.00000000000145E-05 7.00000000000145E-05
Am really clueless on what to do with this situation...any little help will work.
Thanks in advance!
It doesn't seem necessary to me to store them in a different table, you can just calculate them on the fly using APPLY, this way any changes to the underlying data will not cause your change data to be stale:
SELECT T.*,
BidChange = t.Bid - prev.Bid,
AskChange = t.Ask - prev.Ask
FROM T
OUTER APPLY
( SELECT TOP 1 T2.Bid, T2.Ask
FROM T AS T2
WHERE T2.Curr = T.Curr
AND T2.Date < T.Date
ORDER BY T2.Date DESC
) AS prev;
If this is something you will need regularly then you may want to consider a view, rather than storing it in a table.