SQL: Update with minimum value from maximum value - sql

I am struggling to update a column in myTable via VB and OleDB.
myTable:
myGroup Value1 Value2
A 20 5
B 15 3
A 19 4
A 20 6
C 10 2
B 14 4
C 11 7
I want to update column value3 with value2 where value2 is lowest when value1 is highest in group.
I have this:
SELECT myGroup ,MAX(value1), MIN(value2)
FROM myTable
GROUP BY myGroup
I get this:
myGroup Value1 Value2
A 20 4
B 15 3
C 11 2
But I need this:
myGroup Value1 Value2
A 20 5
B 15 3
C 11 7
I need something like "UPDATE mytable SET Value3 = MIN(value2) WHERE MAX(value1) GROUP BY myGroup"
myTabel should be like:
myGroup Value1 Value2 Value3
A 20 5 5
B 15 3 3
A 19 4 5
A 20 6 5
C 10 2 7
B 14 4 3
C 11 7 7
Any help please.

select t.myGroup as myGroup ,value1,min(t.value2) as min2
from stack t
join (select myGroup,max(value1) as max_value1
from stack
group by myGroup) max1_table
on t.myGroup=max1_table.myGroup and t.value1=max1_table.max_value1
group by myGroup,value1

One method is a correlated subquery. Something like this:
select t.*,
(select t2.value2
from t t2
where t2.mygroup = t.mygroup
order by value1 desc, value2 asc
fetch first 1 row only
) as value3
from t;
This uses ANSI/ISO syntax. The specific syntax might vary for your database.
This can also be expressed using the standard function first_value():
select t.*,
first_value(value2) over (partition by mygroup order by value1 desc, value2 asc) as value3
from t;

You might use a query like :
SELECT t1.myGroup, t1.value1, t2.value2 as value3
FROM myTable t1
LEFT JOIN
(
SELECT myGroup, Value1, MIN(VALUE2) AS value2
FROM myTable
WHERE (myGroup, Value1) in
(
SELECT myGroup , MAX(value1)
FROM myTable
GROUP BY myGroup
)
GROUP BY myGroup, Value1) t2
ON ( t1.myGroup = t2.myGroup );
myGroup Value1 Value3
------- ------ ------
A 20 5
B 15 3
A 19 5
A 20 5
C 10 7
B 14 3
C 11 7
Rextester Demo for Select
and with respect to the above SELECT statement, the UPDATE statement might be :
UPDATE myTable t3
SET t3.Value2 =
( SELECT q.value3 FROM
(
SELECT t1.myGroup, t1.value1, t1.value2, t2.value2 as value3
FROM myTable t1
LEFT JOIN
(
SELECT myGroup, Value1, MIN(VALUE2) AS value2
FROM myTable
WHERE (myGroup, Value1) in
(
SELECT myGroup , MAX(value1)
FROM myTable
GROUP BY myGroup
)
GROUP BY myGroup, Value1) t2
ON ( t1.myGroup = t2.myGroup )
) q
WHERE q.myGroup = t3.myGroup
AND q.value1 = t3.value1
AND q.value2 = t3.value2
);
Rextester Demo for Update

Related

SQL - Update Rows from a list of values

So Table Setup is:
Column1 Column2 Column3
A 1 Null
B 2 Null
C 1 Null
D 2 Null
E 1 Null
F 2 Null
G 1 Null
H 2 Null
I would like to update Column3 with an array of values (Value1, Value2, Value3) and cycle through that list until the update is complete
The ultimate goal is for the table to look like this:
Column1 Column2 Column3
A 1 Value1
B 2 Value2
C 1 Value3
D 2 Value1
E 1 Value2
F 2 Value3
G 1 Value1
H 2 Value2
I originally tried in powershell but it was not working as I would have liked because of how the data is being imported, so now I am looking towards SQL. Any suggestions would be great!
You could try an update join here. The approach below is to assign an ordered sequence to both your original table and the "array" of values for updating. We join using modulus logic, such that your table's sequence ordering will match up the values in the array and will wrap around until all values have been assigned.
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (ORDER BY Column1) rn
FROM yourTable
)
UPDATE t1
SET Column3 = t2.val
FROM cte t1
INNER JOIN
(
SELECT 1 AS id, 'Value1' AS val UNION ALL
SELECT 2, 'Value2' UNION ALL
SELECT 3, 'Value3'
) t2
ON t2.id = 1 + ((t1.rn - 1) % 3);
Demo
Assuming you can put the "array" in a table, you can use something like this:
with vals as (
select v.*,
row_number() over (order by (select null)) - 1 as seqnum,
count(*) over () as cnt
from (values ('Value1'), ('Value2'), ('Value3')) v(val)
)
update t
set t.column3 = v.val
from (select t.*,
row_number() over (order by column1) - 1 as seqnum
from t
) t join
vals v
on t.seqnum % v.cnt = v.seqnum;
The basic idea is to enumerate the rows in each table and then use modulo arithmetic to match them.

Select with not exist in the same table with multiple fields

I need to select registers from table that not exist in the same table. I mean if i have this table:
ID VALUE1 VALUE2 VALUE3
1 1 1 1
2 2 2 1
3 3 4 1
4 1 5 1
5 2 2 2
6 3 4 2
7 1 8 2
8 2 2 2
The result of the query should be
ID VALUE1 VALUE2 VALUE3
1 1 1 1
4 1 5 1
Because the rest of the values are the same for the value1 and value2 but diferent value3. I mean the row 2 of the table is the same that the row 5.
I try to do something like but not works:
select t1.value1, t1.value2 from table1 t1 where value3=1
and not exist
(select t2.value1, t2.value2 from table2 t2
where t1.value1=t2.value1 and t1.value2=t2.value2 and value3=2)
Thank you in advise and sorry for my english
You can use the NOT EXISTS as follows:
SELECT *
FROM YOUR_TABLE T1
WHERE T1.VALUE3 = 1
AND NOT EXISTS
(SELECT 1
FROM YOUR_TABLE T2
WHERE T1.VALUE1 = T2.VALUE1
AND T1.VALUE2 = T2.VALUE2)
I think not exists does what you want:
select t1.*
from table1 t1
where t1.value3 = 1 and
not exist (select 1
from table2 t2
where t2.value1 = t1.value1 and
t2.value2 = t1.value2 and
t2.value3 = 2
);
That said, you can also use window functions:
select t1.*
from (select t1.*,
max(value3) over (partition by value1, value2) as max_value3
from table1 t1
where value3 in (1, 2)
) t1
where max_value3 = 1;
I think that you are on the right track with exists.
I would phrase your query like:
select t1.*
from table1 t1
where
t1.value3 = 1
and not exist (
select 1 from table1 t2
where t1.value1 = t2.value1 and t1.value2 = t2.value2 and t2.value3 = 2
)
Key points:
the exists subquery should be from table1 (your query uses table2 but it seems like this table does not actually exists)
you don't actually need to return columns from the exists subquery, since all it does is check if some row is produced - hence the select 1

Count records in query in groups based on column value

Let's suppose a have a very simple query in SQL
SELECT Col1,Col2 From Table1
and it gives me result:
Col1 Col2
A 5
A 7
A 2
B 1
B 1
B 4
B 0
C 4
C 1
C 2
I want to count rows in groups made by Col1 and in order made by Col2. If values in Col2 for some rows in group are equal then they should have different numbers, as shown in example
So I want to have
Col1 Col2 Nr
A 5 2
A 7 3
A 2 1
B 0 1
B 1 2
B 1 3
B 4 4
C 4 3
C 1 1
C 2 2
Any ideas how to make it?
If your database supports window functions, use ROW_NUMBER
select col1,col2,row_number() over(partition by col1 order by col2) as nr
from tablename
If your database doesn't support window functions, use
select col1,col2,
(select count(*)+1 from tablename t1 where t1.col1=t.col1 and t1.col2<t.col2) as nr
from tablename t
You can use the row_number window function:
SELECT col1,
col2,
ROW_NUMBER() OVER (PARTITION BY col1 ORDER BY col2 ASC) AS Nr
FROM table1
ORDER BY 1, 2, 3

SQL Server Update column comparing cross row's value

I have requirement to update column3 of following table by cross checking the value of value2 with next row of value1
If equal then value3 = value1*value2 and if not value3 = value1
CREATE TABLE #tmpValue1(id INT IDENTITY(1,1), value1 FLOAT, value2 FLOAT, value3 FLOAT)
INSERT INTO #tmpValue1(value1, value2) VALUES
(1, 2), (2,3), (3,4), (4,5),(6,7),(7,8),(8,9)
Table #tmpValue1 will be as:
id value1 value2 value3 (expected output)
1 1 2 1
2 2 3 4
3 3 4 9
4 4 5 16
5 6 7 6
6 7 8 49
7 8 9 64
Above, in value3 updated with 1 in first because 2 of Value2 row first is comparing to 2 of value1 of row second so it will start updating with second.
Note: Value1 and Value2 is just sample and is real it can be different.
We can simply do it by using LEFT JOIN as below:
UPDATE t1 SET t1.value3 = (ISNULL(t2.value2,1) * t1.value1)
FROM #tmpValue1 t1
LEFT JOIN #tmpValue1 t2 ON t1.id = t2.id+1
AND t1.value1 = t2.value2
We should use id which is identity column and is beneficial for performing such an operation.
You can use LAG... though your expected output seems to have some errors in it.
select
ID,
Value1,
Value2,
case
when lag(value2) over (order by ID) = value1 then lag(value2) over (order by ID) * value1
else value1 end as Value3
from #tmpValue1
RESULTS
ID Value1 Value2 Value3
1 1 2 1
2 2 3 4
3 3 4 9
4 4 5 16
5 6 7 6
6 7 8 49
7 8 9 64

Count of one of the columns

Let's say I have the dataset that looks like:
col1 col2 col3
a 2 20
a 3 12
a 4 34
b 2 44
c 3 23
c 5 13
....
What I want is a count of col1.
Output:
col1 col2 col3 count
a 2 20 3
a 3 12 3
a 4 34 3
b 2 44 1
c 3 23 2
c 5 13 2
.......
I know I can do by:
with cte as (
select col1, count(*) count
from tab1)
select a.col1,a.col2,a.col3,cte.count
from tab1
join cte on a.col1=cte.col1
But is there any other I can do that without cross apply or cte?
Also, assuming there are more than 3 letters in col1, so I couldn't use sum function either:
SUM(CASE WHEN ItemID = 'a' THEN 1 ELSE 0 END) AS count_a
If you're using SQL Server 2008+, you can use COUNT() OVER():
SELECT *,
COUNT(*) OVER(PARTITION BY col1)
FROM tab1
ONLINE DEMO