I have a table in my oracle DB which has a column with random values. Screenshot is attached below :
I had manually updated the first row to "V0001". Is there any way I can update the rest of the rows to "V0002", "V0003" and so on without manual intervention.
You could use a sequence for this. Create a sequence, convert the sequence's .NEXTVAL to a string, use CONCAT() and UPDATE eg:
Table
create table demo
as
select dbms_random.string( 'x', 11 ) as vehicleid
from dual
connect by level <= 100 ;
select * from demo fetch first 10 rows only ;
-- output
VEHICLEID
LS23XFRNH5N
47DUDNOIRO9
POS5GQSQLMO
BBEEZJMQZI4
2Q8QE30HM2E
S7M5V40YNTD
N2X1YN0OIE3
...
Sequence
create sequence vehicleid_seq start with 1 increment by 1 ;
Update
update demo
set vehicleid = concat( 'V', to_char( vehicleid_seq.nextval, 'FM00000' ) ) ;
Result
select * from demo order by vehicleid fetch first 10 rows only ;
VEHICLEID
V00001
V00002
V00003
V00004
V00005
V00006
V00007
V00008
V00009
V00010
dbfiddle
The identifier code of a table is recommended to be a numeric data, what you could do is an extra field that works as a second code, perhaps called secondary_code. You can do it with Stored Procedure, I give you a small example:
DELIMITER$$
DROP PROCEDURE IF EXISTS sp_genwrar_code$$
CREATE PROCEDURE sp_genwrar_code(
OUT p_secondary_code VARCHAR(4)
)
BEGIN
DECLARE accountant INT;
BEGIN
SET accountant = (SELECT COUNT(*)+1 FROM product);
IF(accountant <10)THEN
SET p_secondary_code= CONCAT('V00',accountant );
ELSE IF(accountant<100) THEN
SET p_secondary_code= CONCAT('V0',accountant);
ELSE IF(accountant<1000)THEN
SET p_secondary_code= CONCAT('V',accountant );
END IF;
END IF;
END IF;
END;
END$$
With that you can generate codes as you need with the structure 'V001'
I have a stored procedure that returns a table. One column in that table is the result of a T-SQL function. That function returns a 0 or 1 based on an ID. The stored procedure accepts IDs stored in XML.
The function is quite complex so I will omit it here, but basically the function ForeignVendor takes an ID and returns 0 or 1.
The function takes an xml argument and returns a table. I need this function as is. I am hoping to reuse it.
Here is the function:
ALTER PROCEDURE ForeignVendors
#VendorIDs xml
AS
BEGIN
SET NOCOUNT ON;
SELECT
VEN_POName,
T.Item.value('#VendorID[1]', 'varchar(10)') AS "VendorID",
ForeignVendor(T.Item.value('#VendorID[1]', 'varchar(10)')) AS "ForeignFlag"
FROM
VEN V
JOIN
#VendorIDs.nodes('/Root/Vendor') AS T(Item) ON V.VEN_VendorID = T.Item.value('#VendorID[1]', 'varchar(10)')
END
I can call it like this
exec [Rotair_ForeignVendors] N'<Root><Vendor VendorID="000010"/><Vendor VendorID="000011"/><Vendor VendorID="000198"/><Vendor VendorID="000021"/></Root>'
and get this back
NEMITZ TOOL CO. 000021 0
P & W CANADA HELICOPTER 000198 1
SHOP 000011 0
ILS 000010 0
One report need to know if any 1's exist in the last column. I was trying to pass the results to a group by query but that does not work.
Any suggestions on the most efficient way to handle this?
How about something like this?
CREATE TABLE #results
(
Vendor VARCHAR(1000),
VendorID varchar(20),
ForeignFlag BIT
);
INSERT INTO #results
EXEC [Rotair_ForeignVendors] N'<Root><Vendor VendorID="000010"/><Vendor VendorID="000011"/><Vendor VendorID="000198"/><Vendor VendorID="000021"/></Root>';
SELECT COUNT(ForeignFlag)
FROM #results
WHERE ForeignFlag = 1;
DROP TABLE #results;
i have a table as follows
Name Points
sam 25
sam 43
sam 44
john 27
john 93
scott 82
scott 45
john 21
john 57
john 33
I want the output as follows
name points
---- ------
sam 37.33
john 60
scott 63.5
john 37
So in the output table i want the value to be printed for every name change in the name column along with the average points for that name.
As specified in the comments, you have to have a way to order the data. I've mocked up a similar dataset only with an IDENTITY field to provide an ordering of the data, as in the data is inserted in a certain order. I create a table store the averages as we iterate through the data, aggregating only the range, by name, before the name changes.
The loop is designed to find the specified range and then perform a simple aggregation. Enjoy.
--Create the main table with an IDENTITY field
IF OBJECT_ID('tbl_Points','U') IS NOT NULL
DROP TABLE tbl_Points;
CREATE TABLE tbl_Points
(
PtID INT IDENTITY
, Name VARCHAR(255)
, Points INT
);
INSERT tbl_Points
VALUES('sam' ,25)
,('sam' ,43)
,('sam' ,44)
,('john' ,27)
,('john' ,93)
,('scott',82)
,('scott',45)
,('john' ,21)
,('john' ,57)
,('john' ,33);
--Creae a table to store the averages as we iterate through the main dataset
IF OBJECT_ID('tbl_AvgPoints','U') IS NOT NULL
DROP TABLE tbl_AvgPoints;
CREATE TABLE tbl_AvgPoints
(
Name VARCHAR(255)
, AVGPoints Numeric(10,4)
);
--Create and initialize the variables
DECLARE
#ptName VARCHAR(255)
, #minPt INT
, #maxPT INT
, #maxRow INT
SET #ptName= ''
SET #minPt = 0
SET #maxPT = 0
--get the max row count for the dataset
SELECT #maxRow=COUNT(1) FROM tbl_Points
--loop through the data until there is nothing left
WHILE #maxPT < #maxRow
BEGIN
--set the bottom of the range to be aggregated
SELECT #minPt=MIN(PtID)
FROM tbl_Points
WHERE PtID > #maxPT
--set the name
SELECT #ptName=Name
FROM tbl_Points
WHERE PtID = #minPt
--Set the starting point to figure out the top of the range
SET #maxPT=#minPt
--Check each record for the name until it changes
WHILE #ptName IS NOT NULL
BEGIN
--stop checking if we have reached the last record
IF #maxPT=#maxRow
SET #ptName = NULL
ELSE
BEGIN
--found the top of the range when the name changes
IF (SELECT Name FROM tbl_Points WHERE PtID = #maxPT) <> #ptName
BEGIN
SET #ptName = NULL
SET #maxPT = #maxPT-1
END
ELSE
SET #maxPT = #maxPT+1
END
END
--Use the bottom and top ranges to aggregate on name
INSERT tbl_AvgPoints
SELECT Name, AVG(CAST(Points AS NUMERIC(10,4))) AvgPoints
FROM tbl_Points
WHERE PtID BETWEEN #minPt AND #maxPT
GROUP BY Name
END
SELECT * FROM tbl_AvgPoints
I update a ClientFunds.Balance table column with a value and use the Ouput Inserted.Balance clause to write the value to a log table.
Occasionally the value written to the ClientFunds table is truncated whilst the value written to the log table is always correct.
First, I fill this table with the details of what will be updated on the ClientFunds table:
declare #ToUpdate table
(
Id int identity(1,1),
ClientFundId int,
Total decimal (18, 2),
Rate decimal (10, 5)
);
This table will hold the data to be written to the log table:
declare #Updated as table
(
ClientFundId int,
AmountBefore decimal (18,2),
AmountAfter decimal (18,2),
ToUpdateId int
);
Here's how I update the ClientFunds table, using the #ToUpdate table and ouput the log information to the #Updated table. I've snipped a few details of the loop here but you should get the idea:
WHILE ##FETCH_STATUS = 0
BEGIN
update cf
set cf.Balance = cf.Balance - (up.Total * up.Rate)
OUTPUT INSERTED.Id, DELETED.Balance, INSERTED.Balance, up.Id
INTO #Updated
from #ToUpdate up
join dbo.ClientFunds cf on cf.Id = up.ClientFundId
where up.Id = #CurrentId
FETCH NEXT FROM ToUpdate_Cursor into #CurrentId;
END
Now, as I said, occasionally the dbo.ClientFunds.Balance value, which is decimal (18, 2), will be written to the db with the decimal part truncated yet the inserted value output to #ToUpdate retains the correct value.
For instance, one example is 14454.36 being written to the #Updated table, and ultimately the log file, but 14454.00 being written to dbo.ClientFunds.Balance.
Any idea of what could be happening?
Thanks,
Jules
I have a stored procedure which drops a table if it exists, then it re-creates the table & fills it with relevant data, a friend of mine has about the same code, the only real difference is in the column headers for the table.
As an illustration, here's how mine looks (not really, just a representation).
+----+-----+-----+--------+
| ID | Foo | Bar | Number |
+----+-----+-----+--------+
| 1 | x | x | 0 |
| 2 | x | x | 1 |
+----+-----+-----+--------+
And here's what his might look like
+----+--------+--------+-----+--------+
| ID | BarFoo | FooBar | Num | Suffix |
+----+--------+--------+-----+--------+
| 1 | x | x | 0 | a |
| 2 | x | x | 1 | b |
+----+--------+--------+-----+--------+
Again, these are merely representations of the situation.
As this is to be a school assignment, the teacher will be creating & executing both SP's, however when creating the SP after using another, I get this error:
Msg 207, Level 16, State 1, Procedure XYZ, Line 59
Invalid column name 'Foo'.
Msg 213, Level 16, State 1, Procedure XYZ, Line 61
Column name or number of supplied values does not match table definition.
However, at the start of both stored procedures, we have this:
CREATE PROCEDURE XYZ
AS
BEGIN
IF EXISTS (SELECT name
FROM sysobjects
WHERE name = 'TABLENAME'
AND xtype = 'u')
DROP TABLE TABLENAME;
From what I understand, this should remove the entire table? Including table/column definitions & data?
The only fix I've found so far, is to either execute the DROP TABLE separately before creating the stored procedure, which won't work for us as it really has to be within the stored procedure.
Help would be much appreciated :)
EDIT: Here's my ACTUAL code, apart from comments, this is exactly how it looks in my script (excluding other code behind it).
IF EXISTS (SELECT name
FROM sysobjects
WHERE name = 'BerekenStatistiek'
AND xtype = 'p')
DROP PROCEDURE BerekenStatistiek;
GO
CREATE PROCEDURE BerekenStatistiek
#jaar INT=0
AS
BEGIN
IF EXISTS (SELECT name
FROM sysobjects
WHERE name = 'Statistiek'
AND xtype = 'u')
DROP TABLE Statistiek;
DECLARE #year AS NVARCHAR (4);
SET #year = CONVERT (NVARCHAR (4), #jaar);
SELECT *,
CAST (Kost - Korting + Freight AS MONEY) AS Netto,
'' AS Richting
INTO Statistiek
FROM (SELECT O.Kwartaal,
CAST (SUM(O.Kost) AS MONEY) AS Kost,
CAST (SUM(O.Korting) AS MONEY) AS Korting,
CAST (SUM(O.Freight) AS MONEY) AS Freight
FROM (SELECT CASE
WHEN CONVERT (NVARCHAR (8), OrderDate, 112) BETWEEN #year + '0101' AND #year + '0331' THEN 1
WHEN CONVERT (NVARCHAR (8), OrderDate, 112) BETWEEN #year + '0401' AND #year + '0630' THEN 2
WHEN CONVERT (NVARCHAR (8), OrderDate, 112) BETWEEN #year + '0701' AND #year + '0930' THEN 3
WHEN CONVERT (NVARCHAR (8), OrderDate, 112) BETWEEN #year + '1001' AND #year + '1231' THEN 4
END AS 'Kwartaal',
ROUND(UnitPrice * Quantity, 2) AS Kost,
Round((UnitPrice * Quantity) * Discount, 2) AS Korting,
Freight
FROM Orders AS O
INNER JOIN
OrderDetails AS Od
ON O.OrderID = Od.OrderID
WHERE CONVERT (NVARCHAR (4), OrderDate, 112) = #year) AS O
GROUP BY O.Kwartaal) AS O1;
ALTER TABLE Statistiek ALTER COLUMN Kwartaal INT NOT NULL;
ALTER TABLE Statistiek ALTER COLUMN Richting NVARCHAR (8);
ALTER TABLE Statistiek
ADD PRIMARY KEY (Kwartaal);
...
And here's his code (the insertion of values in the variables are excluded just for readability (his code is a bit more bulky):
IF EXISTS (SELECT name
FROM sysobjects
WHERE name = 'BerekenStatistiek'
AND xtype = 'p')
BEGIN
DROP PROCEDURE BerekenStatistiek;
END
GO
CREATE PROCEDURE BerekenStatistiek
#jaartal INT
AS
BEGIN
DECLARE #huidigkwartaal AS INT = 1;
DECLARE #beginmaand AS INT;
DECLARE #eindmaand AS INT;
DECLARE #vorige_netto_ontvangsten AS MONEY;
IF EXISTS (SELECT *
FROM sysobjects
WHERE name = 'Statistiek'
AND xtype = 'U')
BEGIN
DROP TABLE Statistiek;
END
CREATE TABLE Statistiek
(
kwartaalnummer INT ,
beginmaand INT ,
eindmaand INT ,
orderbedrag MONEY ,
korting MONEY ,
vervoerskost MONEY ,
netto_ontvangsten MONEY ,
stijgend_dalend_gelijk NVARCHAR (10)
);
--Variables get their data here.
INSERT INTO Statistiek (kwartaalnummer, beginmaand, eindmaand, orderbedrag, korting, vervoerskost, netto_ontvangsten, stijgend_dalend_gelijk)
VALUES (#huidigkwartaal, #beginmaand, #eindmaand, #orderbedrag, #korting, #vervoerskost, #netto_ontvangsten, #stijgend_dalend_gelijk);
"however when creating the SP after using another, I get this error" (Emphasis added.)
SQL Server will insist that a stored procedure match the definitions of tables that exist as the time the stored procedure is created. If the table does not exist when the stored procedure is created, SQL Server will assume that a matching table will appear at run time.
create table t (c int)
go
create procedure p as begin
drop table t
select 1 as diff_column_name into t
select diff_colun_name from t
end
results in:
Msg 207, Level 16, State 1, Procedure p, Line 6
Invalid column name 'diff_colun_name'.
Now, drop table t, and the procedure cane be created:
drop table t
go
create procedure p as begin
drop table t
select 1 as diff_column_name into t
select diff_colun_name from t
end
Command(s) completed successfully.
If you can use a different table name, start with that. And, if the table has to exist only for a moment after the proc is executed so that it can be selected from, then create a global temporary table (i.e. table name starts with ## as in ##MyTable).
However, if it is a requirement to use the same table name as your classmate, then the teacher is probably trying to get you to learn about deferred object resolution (i.e. #Shannon's answer) and how to get around it, because outside of learning this, the scenario makes no sense since one would never do such a thing in reality.
Sub-processes (i.e. EXEC and sp_executesql) do not resolve immediately since they aren't executed when creating the stored procedure. So, simplistically, just declare a new NVARCHAR(MAX) variable to hold some Dynamic SQL and put your SELECT statement in there. Use sp_executesql to pass in the #year variable. You are creating a real table so it will survive beyond the subprocess ending and then the ALTER TABLE statement will work.
Additional notes:
You don't really need the ALTER statement to set the datatype of the [Richting] field. Just tell SQL Server what the type is in your SELECT statement:
CONVERT(NVARCHAR(8), '') AS [Richting]
You don't really want to do CONVERT(NVARCHAR(8), OrderDate, 112) to compare to a value as it invalidates the use of any indexes that might be on [OrderDate]. Instead, construct a date value from the strings and convert that to a DATETIME or DATE (i.e. CONVERT(DATETIME, #year + '0101')).
To better understand this issue, please read Sargability: Why %string% Is Slow, and at least the first link at the bottom, which is: What makes a SQL statement sargable?
You don't really want to convert the OrderDate field to NVARCHAR(4) just to compare the year, for the same reason as just mentioned in the above point. At the very least using the YEAR() function would be more direct. But if you want to make sure indexes can be used, you can't put a function on the field. But you only want the year. So isn't the year the same as BETWEEN #Year + '0101' AND #Year + '1231'? ;-)
Interestingly enough, the first example in the accepted answer in the "What makes a SQL statement sargable?" S.O. question linked in the previous bullet is exactly what I am recommending here :).
For I can understand, the wrong queries are the inserts, because the engine can't find correct table structure, check if the inserts have the same structure of your second table example. Dont forget to check the USE at the beginning of the script, maybe you are using a different db, this can happen :).
In the last bit of code, you are having
AND xtype = 'U'
If your collation is case sensitive, the drop is not taking place and thus the error.