Update A column based on values in B column - sql-server

Let's say I have Table1, and Table2 like below:
Table1
Nmae ColA ColB
---------------
Peter 25
Jason 52
Tom 74
Jim 65
Table2
Values Prize
-------------
25 ABC
50 ABC1
75 ABC2
100 ABC3
Now I want to update ColB in Table1 based on the values in ColA.
e.g. If value >= 25 and < 50 then ABC. If value >= 50 and < 75 then ABC1.
Desired output:
Name ColA ColB
----------------
Peter 25 ABC
Jason 52 ABC1
Tom 76 ABC2
Jim 65 ABC1
I've tried to solve the problem by joining two tables to update ColB but got stuck. I know this can be easily done by using CASE WHEN. However, I'm worried that going forward if the rules change or what, I have to modify the code in multiple SPs.

You can easily do that UPDATE using a subquery instead of a CASE WHEN
update table1 set ColB = (select top 1 Prize
from table2
where Values <= ColA order by Values desc)

Related

SQL Server Join 2 Tables with Relationship in 3rd Table

I have 3 tables:
Table_A
tblA_ID | tblA_Key | tblA_Info
1 2A ABC
Table_B
tblB_ID | tblB_to_A_Relations | tblB_Info
1 1 XYZ
2 1 DEF
3 1 QWE
4 1 NOP
Table_C
tblC_ID | tblC_to_A_Relations | tblC_Info
1 2A 999
2 2A 888
3 2A 777
Table_B and Table_C doesn't have direct relationship but I wanted to join them by using their relationships on Table_A. This is what I've tried so far.
SELECT DISTINCT
b.*,
c.tblC_Info,
FROM Table_B b
LEFT JOIN (SELECT tblA_ID, tblA_Key FROM Table_A
WHERE tblA_Key = #parameter)a
ON a.tblA_ID = b.tblB_to_A_Relations
LEFT JOIN Table_C c
ON c.tblC_to_A_Relations = a.tblA_Key
ORDER BY b.tblB_ID ASC
Somehow outputs:
ResultSet
tblB_ID | tblB_to_A_Relations | tblB_Info | tblC_Info
1 1 XYZ 999
1 1 XYZ 888
1 1 XYZ 777
2 1 DEF 999
2 1 DEF 888
2 1 DEF 777
3 1 QWE 999
3 1 QWE 888
3 1 QWE 777
4 1 NOP 999
4 1 NOP 999
4 1 NOP 999
But my expected output is something like this:
ExpectedOutput
tblB_ID | tblB_to_A_Relations | tblB_Info | tblC_Info
1 1 XYZ NULL
2 1 DEF 999
3 1 QWE 888
4 1 NOP 777
Surely missing something. Any help would really be appreciated!
EDIT
After more inspection of the existing tables and data, it appears that the above results are already filtered records and luckily, after tedious tracing, I found a field that somehow tells them they are unique from the other records.
Table_B
tblB_ID | tblB_to_A_Relations | tblB_Info | **tblB_Values**
1 1 XYZ AAA
2 1 DEF BBB
3 1 QWE CCC
4 1 NOP DDD
Table_C
tblC_ID | tblC_to_A_Relations | tblC_Info | **tblC_Values**
1 2A 999 BBB
2 2A 888 CCC
3 2A 777 DDD
At first, I made a mistake to ignore these fields not knowing the answer was there all along.
Going to post my answer.
Declare #A table
(
tbla_ID int,
tblA_Key varchar(2),
tbla_info varchar(3)
)
Declare #B table
(
tblb_ID int,
tblB_to_A varchar(2),
tblB_info varchar(3)
)
Declare #C table
(
tblb_ID int,
tblC_to_A varchar(2),
tblB_info int
)
Insert into #A
Select 1,'2A','ABC'
Insert into #B
Select 1,1,'XYZ'
union
Select 2,1,'DEF'
union
Select 3,1,'QWE'
union
Select 4,1,'NOP'
Insert into #c
Select 1,'2A',999
UNION
Select 2,'2A',888
UNION
Select 3,'2A',777
Select B.*,C.tblB_info from #A A
join #B B
on A.tbla_ID=B.tblB_to_A
left join #C C
on B.tblb_ID=C.tblb_ID and A.tbla_key=C.tblC_to_A
Null value belongs to 4th row exactly equal to 'NOP'
The workaround I did:
Create a temporary container and update unique keys from Table_B to Table_C in a modified column of a temporary table with reference to Table_A.
Here's what the temporary table looked like:
TempTbl
tblC_ID | Modified_Col | tblC_to_A_Relations | tblC_Info
1 2 2A 999
2 3 2A 888
3 4 2A 777
Managed to get this resultset using a copy of Table_C (#TempTbl)
UPDATE #TempTbl
SET
#TempTbl.Modified_Col = #TempTbl_B.tblB_ID
FROM
#TempTbl
INNER JOIN
#TempTbl_B
ON
#TempTbl.tblB_Values = #TempTbl_B.tblC_Values
And pulled left join for final result.
SELECT * FROM Table_B x
LEFT JOIN #TempTbl y
ON x.tblB_ID = y.Modified_Col
PS: I really do apologize how horrible the workaround was.

Update table in sql query

I am struggling with the following (apparently) easy task; my table (call it table1) look like this:
id | date | value
------------------
1 | 01 | 100
1 | 02 | 103
1 | 04 | 105
1 | 05 | 90
1 | 06 | 95
1 | 09 | 0
2 | 02 | 110
2 | 03 | 98
2 | 04 | 97
2 | 07 | 71
2 | 08 | 84
2 | 10 | 0
------------------
I would like to replace the two 0s with, respectively 95 and 84 (i.e. previous values in time). Any solution? I have been spending a looot of time on this (sorry but I am quite new to SQL)
Try this:
update table1 as a
set a.value=(select b.value
from table1 as b
where b.date<=a.date order by b.date desc limit 1)
where a.value=0;
Change to this
Make a new replica of table1 as table2 (same structure & same data in table1 and table2):
SET SQL_SAFE_UPDATES = 0;
update table1
set value=(select value
from table2
where table2.date<table1.date order by table2.date desc limit 1)
where table1.value=0;
Building up on nacho's query:
MySQL's update is a bit flawed. First: it doesn't accept an alias for the updated table. Second: It complains when you access the same table you want to update in the query. The latter can easily be solved by replacing from table1 b with from (select * from table1) b.
nacho's statement also missed to have the subquery refer to the same ID. And he mistakenly included the record itself (b.date <= instead of b.date <).
update table1
set value =
(
select b.value
from (select * from table1) b
where b.id = table1.id and b.date < table1.date
order by b.date desc limit 1
)
where value = 0;
And here is a test: http://rextester.com/YQV55035
UPDATE: You obviously tagged the wrong DBMS. Here is the same query for SQL Server:
update table1
set value =
(
select top(1) b.value
from table1 b
where b.id = table1.id and b.date < table1.date
order by b.date desc
)
where value = 0;

Sql create table with alternate row from two different table

I'm having two table which contain data for reviler for employee on shift basis
e.g
Table 1
------
ID NAME RELIVERID
------------
20 ABC 56
----------
21 XYZ 57
----------
22 DEF 58
----------
TABLE 2
---------
ID NAME RELIVERID
-------
56 PQR 20
-----
57 STU 21
-----
58 XYZ 21
----
I want result in third table with following data
Result Table
-------
ID NAME RELIVERID
---
20 ABC 56
-
56 PQR 20
-
21 XYZ 57
-
57 STU 21
-
22 DEF 58
-
58 XYZ 21
-
1 row from first table and alternate row from second table
My suggestion is to use the row_number function, multiply it with a factor for the 1st table and for the second table add 1 so it will be greater than the one in the 1st table and perform an union all. I don't a SQL Server instance to test this, but it should be something like this:
SELECT (ROW_NUMBER() OVER (ORDER BY ID ASC)) * 100 AS OrderID, * FROM Table1
UNION ALL
SELECT (ROW_NUMBER() OVER (ORDER BY ID ASC)) * 100 + 1 AS OrderID, * FROM Table2
ORDER BY OrderID

How to add a calculated column of certain rows in SQL

I want to add a calculated column (persisted) that is the total rows for the same group of categories such as sales order below. How would you do this in SQL Server?
SalesOrder Amount Total(calculated)
100 10 25
100 15 25
101 20 45
101 25 45
102 30 65
102 35 65
The best mechanism to use for storing pre-calculated aggregates that are automatically maintained would be an indexed view, it is not possible via a persisted computed column (you could use a scalar UDF in a computed column to calculate the result but this can't be persisted and such computed columns are generally bad for performance both as they force RBAR evaluation and as they block parallelism).
CREATE VIEW dbo.AggregatedSales
WITH SCHEMABINDING
AS
SELECT SalesOrder,
SUM(Amount) AS Total
FROM dbo.YourTable
GROUP BY SalesOrder
GO
CREATE UNIQUE CLUSTERED INDEX UIX ON dbo.AggregatedSales(SalesOrder)
Then the aggregates will be pre calculated and stored in the view. Your queries will need to join on the view. You may need to use the NOEXPAND hint to be sure that the pre calculated aggregates are in fact used and they aren't recalculated at runtime.
For SQL server 2012
CREATE TABLE #t (saleOrder int , amount int)
INSERT INTO #t VALUES
(100,10)
,(100,15)
,(101,20)
,(101,25)
,(102,30)
,(102,35)
SELECT *
,SUM(amount) OVER (PARTITION BY saleorder) as [total]
FROM #t
Result :
saleOrder | amount | total
==========================
100 | 10 | 25
100 | 15 | 25
101 | 20 | 45
101 | 25 | 45
102 | 30 | 65
102 | 35 | 65

sql query to delete only one duplicate row

I've a table with some duplicate rows in it. I want to delete only one duplicate row.
For example I'v 9 duplicate rows so should delete only one row and should show 8 remaining rows.
example
date calling called duration timestampp
2012-06-19 10:22:45.000 165 218 155 1.9 121
2012-06-19 10:22:45.000 165 218 155 1.9 121
2012-06-19 10:22:45.000 165 218 155 1.9 121
2012-06-19 10:22:45.000 165 218 155 1.9 121
from above date should delete only one row and should show 3 rows
2012-06-19 10:22:45.000 165 218 155 1.9 100
2012-06-19 10:22:45.000 165 218 155 1.9 100
2012-06-19 10:22:45.000 165 218 155 1.9 100
from above date should delete only one row and should show 2 rows
How can I do this?
This solution allows you to delete one row from each set of duplicates (rather than just handling a single block of duplicates at a time):
;WITH x AS
(
SELECT [date], rn = ROW_NUMBER() OVER (PARTITION BY
[date], calling, called, duration, [timestamp]
ORDER BY [date])
FROM dbo.UnspecifiedTableName
)
DELETE x WHERE rn = 2;
As an aside, both [date] and [timestamp] are terrible choices for column names...
For SQL Server 2005+ you can do the following:
;WITH CTE AS
(
SELECT *,
ROW_NUMBER() OVER(PARTITION BY [date], calling, called, duration, [timestamp] ORDER BY 1) RN
FROM YourTable
)
DELETE FROM CTE
WHERE RN = 2
Do you have a primary key on the table?
What makes a row a duplicate? Same time? same date? all columns being the same?
If you have a primary key you can use the TOP function to select only one record and delete that one row:
Delete from [tablename] where id in (select top 1 id from [tablename] where [clause])
If you don't mind the order of these rows there is a command in MySQL:
DELETE TOP (numberOfRowsToDelete) FROM db.tablename WHERE {condition for ex id = 5};
Since I don't have the schema, I'd a possible solution in steps:
Apply a row number to the select of all columns
Make a group by with those columns and delete the min(rownumber) in each group
Edit:
The rownumber is in a inner query and will have the rownumber incrementing in all rows. In the outer query I make the group by of the inner query and select the min(rownumber) for each group. Since each group is composed by duplicated rows, I then remove the min(rownumber) for each group.
using LIMIT 1 will help you delete only 1 ROW that matches your DELETE query:
DELETE FROM `table_name` WHERE `column_name`='value' LIMIT 1;
BEFORE:
+----------------------+
| id | column_name |
+-----+----------------+
| 1 | value |
+-----+----------------+
| 2 | value |
+-----+----------------+
| 3 | value |
+-----+----------------+
| 4 | value |
+-----+----------------+
AFTER:
+----------------------+
| id | column_name |
+-----+----------------+
| 1 | value |
+-----+----------------+
| 2 | value |
+-----+----------------+
| 3 | value |
+-----+----------------+

Resources