Related
I'm currently working on a stored procedure on SQL Server 2016. In my Database I have a table structure and need to add another table, which references to the same table as an existing one.
Thus, I have 2 times a 1:1 relation to the same table.
The occuring problem is, I reference the same keys from 2 different origin tables twice in the same target table.
Target table:
FK_Tables | Text
----------------
1 | Table One Text Id: 1
1 | Table Two Text Id: 1 // The error: Same FK_Tables 2 times
Table One:
ID | OtherField
---------
1 | 42
Table Two:
ID | CoolField
---------
1 | 22
Table One and Table Two are currently referencing to the table Reference Table.
Do you know how I can solve this problem, of the same ID twice?
Thanks!!
You need to add a column for each table you're referencing, otherwise you wouldn't know where the ID is coming from if they were all inserted into the same field. Something like this:
/*
CREATE TEST TABLES
*/
DROP TABLE IF EXISTS tbOne;
CREATE TABLE tbOne ( ID INT IDENTITY(1,1) NOT NULL PRIMARY KEY
, TXT VARCHAR(10)
);
DROP TABLE IF EXISTS tbTwo;
CREATE TABLE tbTwo ( ID INT IDENTITY(1,1) NOT NULL PRIMARY KEY
, TXT VARCHAR(10)
);
DROP TABLE IF EXISTS Target;
CREATE TABLE Target ( ID INT IDENTITY(1,1) NOT NULL PRIMARY KEY
, FKTB1 INT
, FKTB2 INT
, TXT VARCHAR(100)
);
-- 1st FK tbOne
ALTER TABLE Target ADD CONSTRAINT FK_One FOREIGN KEY (FKTB1) REFERENCES tbOne (ID);
--2nd FK tbTwo
ALTER TABLE Target ADD CONSTRAINT FK_Two FOREIGN KEY (FKTB2) REFERENCES tbTwo (ID);
-- Populate test tables
INSERT INTO tbOne (TXT)
SELECT TOP 100 LEFT(text, 10)
FROM SYS.messages
INSERT INTO tbTwo (TXT)
SELECT TOP 100 LEFT(text, 10)
FROM SYS.messages
INSERT INTO [Target] (FKTB1, FKTB2, TXT)
SELECT 1, 1, 'Test - constraint'
-- Check result set
SELECT *
FROM tbTwo
SELECT *
FROM tbOne
SELECT *
FROM [Target] T
INNER JOIN tbOne TB1
ON T.FKTB1 = TB1.ID
INNER JOIN tbTwo TB2
ON T.FKTB2 = TB2.ID
I have the following SQL command, it works with H2 database, but when i try to run it on Oracle XE, it gets the " ORA-00911: invalid character " error.
create table EMPLOYEE (
EMPLOYEE_KEY NUMBER(10) not null,
SALARY NUMBER(10,2),
LAST_NAME VARCHAR2(132),
FIRST_NAME VARCHAR2(132),
SUPERVISOR_KEY NUMBER(10),
constraint EMPLOYEE_PK primary key (EMPLOYEE_KEY)
);
create unique index EMPLOYEE_PK on EMPLOYEE(EMPLOYEE_KEY);
With this example, when you created the primary key constraint, you also created an index with the same name as the index you are trying to create, EMPLOYEE_PK.
Tom Kytes states on his ask tom site:
A primary key or unique constraint is not guaranteed to create a new
index, nor is the index they create guaranteed to be a unique index.
Therefore, if you desire a unique index to be created for query
performance issues, you should explicitly create one.
Oddly, enough when I run your DDL, I receive the ORA-00955 error and not the ORA-00911 (invalid character error).
Often times the ORA-00911 error occurs when one is copying from one editor to another and you copy some non-printable characters.
Below, I ran the first DDL statement provided and then I checked the indices created. If you look closely, you will see that EMPLOYEE_PK index was created as a consequence of the primary key constraint which you created.
SCOTT#dev> create table EMPLOYEE (
2 EMPLOYEE_KEY NUMBER(10) not null,
3 SALARY NUMBER(10,2),
4 LAST_NAME VARCHAR2(132),
5 FIRST_NAME VARCHAR2(132),
6 SUPERVISOR_KEY NUMBER(10),
7 constraint EMPLOYEE_PK primary key (EMPLOYEE_KEY)
8 );
Table created.
SCOTT#dev> SELECT ind.index_name,
2 ind.index_type,
3 ind.table_owner,
4 ind.table_name
5 FROM all_indexes ind
6 JOIN all_ind_columns icol
7 ON ind.owner = icol.index_owner
8 AND ind.table_name = icol.table_name
9 AND ind.index_name = icol.index_name
10 WHERE 1 = 1
11 AND ind.table_name = 'EMPLOYEE'
12 /
INDEX_NAME INDEX_TYPE TABLE_OWNER TABLE_NAME
============================== =========================== ============================== ==============================
EMPLOYEE_PK NORMAL SCOTT EMPLOYEE
If you desire to create a unique index on EMPLOYEE_KEY separately, a number of approaches could be taken. Here is one:
--create the table
SCOTT#dev> create table EMPLOYEE (
2 EMPLOYEE_KEY NUMBER(10) not null,
3 SALARY NUMBER(10,2),
4 LAST_NAME VARCHAR2(132),
5 FIRST_NAME VARCHAR2(132),
6 SUPERVISOR_KEY NUMBER(10)
7 );
Table created.
--create the unique index
SCOTT#dev> CREATE UNIQUE INDEX EMPLOYEE_PK ON EMPLOYEE (EMPLOYEE_KEY);
Index created.
--add the primary key
SCOTT#dev> alter table EMPLOYEE add
2 constraint EMPLOYEE_PK primary key (EMPLOYEE_KEY)
3 /
Table altered.
SCOTT#dev> SELECT ind.index_name,
2 ind.index_type,
3 ind.table_owner,
4 ind.table_name
5 FROM all_indexes ind
6 JOIN all_ind_columns icol
7 ON ind.owner = icol.index_owner
8 AND ind.table_name = icol.table_name
9 AND ind.index_name = icol.index_name
10 WHERE 1 = 1
11 AND ind.table_name = 'EMPLOYEE'
12 /
INDEX_NAME INDEX_TYPE TABLE_OWNER TABLE_NAME
============================== =========================== ============================== ==============================
EMPLOYEE_PK NORMAL SCOTT EMPLOYEE
SCOTT#dev>
SCOTT#dev> SELECT cons.constraint_name
2 FROM all_constraints cons
3 JOIN all_cons_columns conc
4 ON conc.table_name = 'EMPLOYEE'
5 AND cons.owner = conc.owner
6 AND cons.table_name = conc.table_name
7 WHERE 1 = 1
8 AND cons.constraint_name = conc.constraint_name
9 /
CONSTRAINT_NAME
=============================
EMPLOYEE_PK
I won' insist on the redundancy between a primary key and an unique index, but, to answer only to your question as it is titled:
when i try to run it on Oracle XE, it gets the " ORA-00911: invalid character " error.
It chops on ; as, as far as I know, you can only issue one SQL command at a time through the "SQL Workshop > SQL Command" page of Oracle application Express (you can send a PL/SQL bloc too)
However you can select your commands one by one and hit run. When there is a selection, only the sectioned part is executed. For example, in the following screen capture, only the first DDL statement will be executed by clicking on the "run" button:
I have a view for which I want to create an Indexed view. After a lot of energy I was able to put the sql query in place for the view and It looks like this -
ALTER VIEW [dbo].[FriendBalances] WITH SCHEMABINDING as
WITH
trans (Amount,PaidBy,PaidFor, Id) AS
(SELECT Amount,userid AS PaidBy, PaidForUsers_FbUserId AS PaidFor, Id FROM dbo.Transactions
FULL JOIN dbo.TransactionUser ON dbo.Transactions.Id = dbo.TransactionUser.TransactionsPaidFor_Id),
bal (PaidBy,PaidFor,Balance) AS
(SELECT PaidBy,PaidFor, SUM( Amount/ transactionCounts.[_count]) AS Balance FROM trans
JOIN (SELECT Id,COUNT(*)AS _count FROM trans GROUP BY Id) AS transactionCounts ON trans.Id = transactionCounts.Id AND trans.PaidBy <> trans.PaidFor
GROUP BY trans.PaidBy,trans.PaidFor )
SELECT ISNULL(bal.PaidBy,bal2.PaidFor)AS PaidBy,ISNULL(bal.PaidFor,bal2.PaidBy)AS PaidFor,
ISNULL( bal.Balance,0)-ISNULL(bal2.Balance,0) AS Balance
FROM bal
left JOIN bal AS bal2 ON bal.PaidBy = bal2.PaidFor AND bal.PaidFor = bal2.Paidby
WHERE ISNULL( bal.Balance,0)>ISNULL(bal2.Balance,0)
Sample Data for FriendBalances View -
PaidBy PaidFor Balance
------ ------- -------
9990 9991 1000
9990 9992 2000
9990 9993 1000
9991 9993 1000
9991 9994 1000
It is mainly a join of 2 tables.
Transactions -
CREATE TABLE [dbo].[Transactions](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Date] [datetime] NOT NULL,
[Amount] [float] NOT NULL,
[UserId] [bigint] NOT NULL,
[Remarks] [nvarchar](255) NULL,
[GroupFbGroupId] [bigint] NULL,
CONSTRAINT [PK_Transactions] PRIMARY KEY CLUSTERED
Sample data in Transactions Table -
Id Date Amount UserId Remarks GroupFbGroupId
-- ----------------------- ------ ------ -------------- --------------
1 2001-01-01 00:00:00.000 3000 9990 this is a test NULL
2 2001-01-01 00:00:00.000 3000 9990 this is a test NULL
3 2001-01-01 00:00:00.000 3000 9991 this is a test NULL
TransactionUsers -
CREATE TABLE [dbo].[TransactionUser](
[TransactionsPaidFor_Id] [bigint] NOT NULL,
[PaidForUsers_FbUserId] [bigint] NOT NULL
) ON [PRIMARY]
Sample Data in TransactionUser Table -
TransactionsPaidFor_Id PaidForUsers_FbUserId
---------------------- ---------------------
1 9991
1 9992
1 9993
2 9990
2 9991
2 9992
3 9990
3 9993
3 9994
Now I am not able to create a view because my query contains cte(s). What are the options that I have now?
If cte can be removed, what should be the other option which would help in creating indexed views.
Here is the error message -
Msg 10137, Level 16, State 1, Line 1 Cannot create index on view "ShareBill.Test.Database.dbo.FriendBalances" because it references common table expression "trans". Views referencing common table expressions cannot be indexed. Consider not indexing the view, or removing the common table expression from the view definition.
The concept:
Transaction mainly consists of:
an Amount that was paid
UserId of the User who paid that amount
and some more information which is not important for now.
TransactionUser table is a mapping between a Transaction and a User Table. Essentially a transaction can be shared between multiple persons. So we store that in this table.
So we have transactions where 1 person is paying for it and other are sharing the amount. So if A pays 100$ for B then B would owe 100$ to A. Similarly if B pays 90$ for A then B would owe only $10 to A. Now if A pays 300$ for A,b,c that means B would owe 110$ and C would owe 10$ to A.
So in this particular view we are aggregating the effective amount that has been paid (if any) between 2 users and thus know how much a person owes another person.
Okay, this gives you an indexed view (that needs an additional view on top of to sort out the who-owes-who detail), but it may not satisfy your requirements still.
/* Transactions table, as before, but with handy unique constraint for FK Target */
CREATE TABLE [dbo].[Transactions](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Date] [datetime] NOT NULL,
[Amount] [float] NOT NULL,
[UserId] [bigint] NOT NULL,
[Remarks] [nvarchar](255) NULL,
[GroupFbGroupId] [bigint] NULL,
CONSTRAINT [PK_Transactions] PRIMARY KEY CLUSTERED (Id),
constraint UQ_Transactions_XRef UNIQUE (Id,Amount,UserId)
)
Nothing surprising so far, I hope
/* Much expanded TransactionUser table, we'll hide it away and most of the maintenance is automatic */
CREATE TABLE [dbo]._TransactionUser(
[TransactionsPaidFor_Id] int NOT NULL,
[PaidForUsers_FbUserId] [bigint] NOT NULL,
Amount float not null,
PaidByUserId bigint not null,
UserCount int not null,
LowUserID as CASE WHEN [PaidForUsers_FbUserId] < PaidByUserId THEN [PaidForUsers_FbUserId] ELSE PaidByUserId END,
HighUserID as CASE WHEN [PaidForUsers_FbUserId] < PaidByUserId THEN PaidByUserId ELSE [PaidForUsers_FbUserId] END,
PerUserDelta as (Amount/UserCount) * CASE WHEN [PaidForUsers_FbUserId] < PaidByUserId THEN -1 ELSE 1 END,
constraint PK__TransactionUser PRIMARY KEY ([TransactionsPaidFor_Id],[PaidForUsers_FbUserId]),
constraint FK__TransactionUser_Transactions FOREIGN KEY ([TransactionsPaidFor_Id]) references dbo.Transactions,
constraint FK__TransactionUser_Transaction_XRef FOREIGN KEY ([TransactionsPaidFor_Id],Amount,PaidByUserID)
references dbo.Transactions (Id,Amount,UserId) ON UPDATE CASCADE
)
This table now maintains enough information to allow the view to be constructed. The rest of the work we do is to construct/maintain the data in the table. Note that, with the foreign key constraint, we've already ensured that if, say, an amount is changed in the transactions table, everything gets recalculated.
/* View that mimics the original TransactionUser table -
in fact it has the same name so existing code doesn't need to change */
CREATE VIEW dbo.TransactionUser
with schemabinding
as
select
[TransactionsPaidFor_Id],
[PaidForUsers_FbUserId]
from
dbo._TransactionUser
GO
/* Effectively the PK on the original table */
CREATE UNIQUE CLUSTERED INDEX PK_TransactionUser on dbo.TransactionUser ([TransactionsPaidFor_Id],[PaidForUsers_FbUserId])
Anything that's already written to work against TransactionUser will now work against this view, and be none the wiser. Except, they can't insert/update/delete the rows without some help:
/* Now we write the trigger that maintains the underlying table */
CREATE TRIGGER dbo.T_TransactionUser_IUD
ON dbo.TransactionUser
INSTEAD OF INSERT, UPDATE, DELETE
AS
SET NOCOUNT ON;
/* Every delete affects *every* row for the same transaction
We need to drop the counts on every remaining row, as well as removing the actual rows we're interested in */
WITH DropCounts as (
select TransactionsPaidFor_Id,COUNT(*) as Cnt from deleted group by TransactionsPaidFor_Id
), KeptRows as (
select tu.TransactionsPaidFor_Id,tu.PaidForUsers_FbUserId,UserCount - dc.Cnt as NewCount
from dbo._TransactionUser tu left join deleted d
on tu.TransactionsPaidFor_Id = d.TransactionsPaidFor_Id and
tu.PaidForUsers_FbUserId = d.PaidForUsers_FbUserId
inner join DropCounts dc
on
tu.TransactionsPaidFor_Id = dc.TransactionsPaidFor_Id
where
d.PaidForUsers_FbUserId is null
), ChangeSet as (
select TransactionsPaidFor_Id,PaidForUsers_FbUserId,NewCount,1 as Keep
from KeptRows
union all
select TransactionsPaidFor_Id,PaidForUsers_FbUserId,null,0
from deleted
)
merge into dbo._TransactionUser tu
using ChangeSet cs on tu.TransactionsPaidFor_Id = cs.TransactionsPaidFor_Id and tu.PaidForUsers_FbUserId = cs.PaidForUsers_FbUserId
when matched and cs.Keep = 1 then update set UserCount = cs.NewCount
when matched then delete;
/* Every insert affects *every* row for the same transaction
This is why the indexed view couldn't be generated */
WITH TU as (
select TransactionsPaidFor_Id,PaidForUsers_FbUserId,Amount,PaidByUserId from dbo._TransactionUser
where TransactionsPaidFor_Id in (select TransactionsPaidFor_Id from inserted)
union all
select TransactionsPaidFor_Id,PaidForUsers_FbUserId,Amount,UserId
from inserted i inner join dbo.Transactions t on i.TransactionsPaidFor_Id = t.Id
), CountedTU as (
select TransactionsPaidFor_Id,PaidForUsers_FbUserId,Amount,PaidByUserId,
COUNT(*) OVER (PARTITION BY TransactionsPaidFor_Id) as Cnt
from TU
)
merge into dbo._TransactionUser tu
using CountedTU new on tu.TransactionsPaidFor_Id = new.TransactionsPaidFor_Id and tu.PaidForUsers_FbUserId = new.PaidForUsers_FbUserId
when matched then update set Amount = new.Amount,PaidByUserId = new.PaidByUserId,UserCount = new.Cnt
when not matched then insert
([TransactionsPaidFor_Id],[PaidForUsers_FbUserId],Amount,PaidByUserId,UserCount)
values (new.TransactionsPaidFor_Id,new.PaidForUsers_FbUserId,new.Amount,new.PaidByUserId,new.Cnt);
Now that the underlying table is being maintained, we can finally write the indexed view you wanted in the first place... almost. The issue is that the totals we create may be positive or negative, because we've normalized the transactions so that we can easily sum them:
CREATE VIEW [dbo]._FriendBalances
WITH SCHEMABINDING
as
SELECT
LowUserID,
HighUserID,
SUM(PerUserDelta) as Balance,
COUNT_BIG(*) as Cnt
FROM dbo._TransactionUser
WHERE LowUserID != HighUserID
GROUP BY
LowUserID,
HighUserID
GO
create unique clustered index IX__FriendBalances on dbo._FriendBalances (LowUserID, HighUserID)
So we finally create a view, built on the indexed view above, that if the balance is negative, we flip the person owed, and the person owing around. But it will use the index on the above view, which is most of the work we were seeking to save by having the indexed view:
create view dbo.FriendBalances
as
select
CASE WHEN Balance >= 0 THEN LowUserID ELSE HighUserID END as PaidBy,
CASE WHEN Balance >= 0 THEN HighUserID ELSE LowUserID END as PaidFor,
ABS(Balance) as Balance
from
dbo._FriendBalances WITH (NOEXPAND)
Now, finally, we insert your sample data:
set identity_insert dbo.Transactions on --Ensure we get IDs we know
GO
insert into dbo.Transactions (Id,[Date] , Amount , UserId , Remarks ,GroupFbGroupId)
select 1 ,'2001-01-01T00:00:00.000', 3000, 9990 ,'this is a test', NULL union all
select 2 ,'2001-01-01T00:00:00.000', 3000, 9990 ,'this is a test', NULL union all
select 3 ,'2001-01-01T00:00:00.000', 3000, 9991 ,'this is a test', NULL
GO
set identity_insert dbo.Transactions off
GO
insert into dbo.TransactionUser (TransactionsPaidFor_Id, PaidForUsers_FbUserId)
select 1, 9991 union all
select 1, 9992 union all
select 1, 9993 union all
select 2, 9990 union all
select 2, 9991 union all
select 2, 9992 union all
select 3, 9990 union all
select 3, 9993 union all
select 3, 9994
And query the final view:
select * from dbo.FriendBalances
PaidBy PaidFor Balance
9990 9991 1000
9990 9992 2000
9990 9993 1000
9991 9993 1000
9991 9994 1000
Now, there is additional work we could do, if we were concerned that someone may find a way to dodge the triggers and perform direct changes to the base tables. The first would be yet another indexed view, that will ensure that every row for the same transaction has the same UserCount value. Finally, with a few additional columns, check constraints, FK constraints and more work in the triggers, I think we can ensure that the UserCount is correct - but it may add more overhead than you want.
I can add scripts for these aspects if you want me to - it depends on how restrictive you want/need the database to be.
I was wondering whether there is a possibility for me to discover the underlying primary (or unique) key columns for all tables involved in an Oracle view. Here's an example to show what I mean:
CREATE TABLE t_a (
id number(7),
primary key(id)
);
CREATE VIEW v_a AS
SELECT * FROM t_a;
So by naming convention, I know that v_a.id is actually the primary key column of the underlying t_a table. Is there any way of formally discovering this information by using system views, such as SYS.ALL_CONSTRAINTS, SYS.USER_CONSTRAINTS, etc?
N.B:
The constraints are NOT on the view, but on the underlying table.
I'm not interested in the keys themselves, but in the columns of the view.
You can find that information via the user_dependencies view:
SQL> CREATE TABLE t_a
2 ( id number(7)
3 , primary key(id)
4 )
5 /
Table created.
SQL> CREATE VIEW v_a AS SELECT * FROM t_a
2 /
View created.
SQL> select c.constraint_name
2 from user_dependencies d
3 , all_constraints c
4 where d.name = 'V_A'
5 and d.referenced_type = 'TABLE'
6 and d.referenced_link_name is null
7 and d.referenced_owner = c.owner
8 and d.referenced_name = c.table_name
9 and c.constraint_type = 'P'
10 /
CONSTRAINT_NAME
------------------------------
SYS_C0051559
1 row selected.
Regards,
Rob.
EDIT: For possible view column names, you can use this query. Note there is no guarantee that such a column exists in your view.
SQL> select c.constraint_name
2 , 'V_' || substr(c.table_name,3) || '.' || cc.column_name possible_view_column
3 from user_dependencies d
4 , all_constraints c
5 , all_cons_columns cc
6 where d.name = 'V_A'
7 and d.referenced_type = 'TABLE'
8 and d.referenced_link_name is null
9 and d.referenced_owner = c.owner
10 and d.referenced_name = c.table_name
11 and c.constraint_type = 'P'
12 and c.owner = cc.owner
13 and c.constraint_name = cc.constraint_name
14 /
CONSTRAINT_NAME POSSIBLE_VIEW_COLUMN
------------------------------ -------------------------------------------------------------
SYS_C0051561 V_A.ID
1 row selected.
SELECT column_name FROM user_ind_columns
WHERE index_name =
(SELECT index_name FROM user_constraints
WHERE constraint_type='P' AND table_name='T_A')
ORDER BY column_position;
If you want to do it for all tables that a view depends on, then change the condition on table_name to something like:
table_name IN (SELECT referenced_name FROM user_dependencies
WHERE name='view_name' AND referenced_type='TABLE')
SQL Server 2000
Background
I've got a table that stores miscellaneous meta data about a specific course in my course table. The table is defined:
create table course_prefs {
id int identity not null,
crs_nbr int references course (crs_nbr) not null,
fiscal_yr int not null,
group_name varchar(50) not null,
item_name varchar(50) null,
value varchar(100) not null)
and there are some values like so:
ID Crs_Nbr Fiscal_Yr Group_Name Item_Name Value
1 5327 2007 StuAchievement Qualifier alg
2 5329 2007 StuAchievement Qualifier alg
153 2000 2003 LocUCInfo 543 F,0,0
154 2000 2003 LocUCInfo 542 F,0,0
6149 15746 2009 summerAttn HS coreClass
6150 12367 2009 summerAttn HS coreClass
...and I've begun making views from this prefs table to suit the specific needs. However, when I join to the following view:
CREATE view loc_uc_info as
select cp.crs_nbr, c.abbr, cp.fiscal_yr, convert(int,cp.item_name) as loc_id
, substring(cp.value,1,1) as subject_area
, substring(cp.value,3,1) as honors
, substring(cp.value,5,1) as can_be_elective
from course_prefs cp join course c on cp.crs_nbr = c.crs_nbr
where cp.group_name = 'LocUCInfo'
The Problem
I get the following error message:
Syntax error converting the varchar value 'HS' to a column of data type smallint.
What I Want
I need to write a query that joins to this view on the loc_id column. This means that both the parent table and the view are joined on columns typed as integers. BUT - the view has both integer and char values in the item_name column thus, I get the syntax error. What can I do to get around this?
Things I've Tried:
Using a derived query in place of the view and I get the same error.
Creating another view based solely on the uc_loc_info view. Got same error.
Using the isnumeric(cp.item_name) = 1 where clause in my loc_uc_info view to restrict the results.
Not really sure what you want the outcome to be but what about using:
case when isnumeric(cp.item_name) = 1 then convert(int,cp.item_name) else null end
instead of just your
convert(int,cp.item_name)
Try this :
convert(int,case when isnumeric(cp.item_name)= 1 then cp.item_name else null end as loc_id
If that doesn't work try this:
convert(int,case when isnumeric(cp.item_name)= 1 then cp.item_name else 0 end as loc_id
Personally I believe something is very flawed about your basic design, you shouldn't have numerics and character data in the same column like that. Nor should you have comma delimited values.
And I'm not a fan of views, especially views that get put on top of views as they can kill performance when they can't be properly indexed.
Note: Final working code added below first message.
Can you explain more what you're trying to accomplish with this line in your view?
convert(int, cp.item_name) as loc_id,
Penfold's suggestion seems like a good one.
Here is working code. (Yes, it uses 2005 "sys." tables. Convert those to run on 2000.) It replaces your "loc_id" column with Penfold's suggestion.
Code
IF EXISTS (SELECT * FROM sys.tables WHERE name = 'Course')
DROP TABLE dbo.Course
GO
CREATE TABLE dbo.Course (
ID int not null, -- identity
Abbr varchar(5) not null,
Crs_Nbr int not null --references course (crs_nbr)
)
GO
IF EXISTS (SELECT * FROM sys.tables WHERE name = 'Course_Prefs')
DROP TABLE dbo.Course_Prefs
GO
CREATE TABLE dbo.Course_Prefs (
ID int not null, -- identity
Crs_Nbr int not null, --references course (crs_nbr)
Fiscal_Yr int not null,
Group_Name varchar(50) not null,
Item_Name varchar(50) null,
Value varchar(100) not null
)
GO
INSERT INTO dbo.Course VALUES (1, 'Crs1', 5327)
INSERT INTO dbo.Course VALUES (2, 'Crs2', 5329)
INSERT INTO dbo.Course VALUES (3, 'Crs3', 2000)
INSERT INTO dbo.Course VALUES (4, 'Crs4', 15746)
INSERT INTO dbo.Course VALUES (5, 'Crs5', 12367)
GO
INSERT INTO dbo.Course_Prefs VALUES (1, 5327, 2007, 'StuAchievement', 'Qualifier', 'alg')
INSERT INTO dbo.Course_Prefs VALUES (2, 5329, 2007, 'StuAchievement', 'Qualifier', 'alg')
INSERT INTO dbo.Course_Prefs VALUES (153, 2000, 2003, 'LocUCInfo', '543', 'F,0,0')
INSERT INTO dbo.Course_Prefs VALUES (154, 2000, 2003, 'LocUCInfo', '542', 'F,0,0')
INSERT INTO dbo.Course_Prefs VALUES (6149, 15746, 2009, 'summerAttn', 'HS', 'coreClass')
INSERT INTO dbo.Course_Prefs VALUES (6150, 12367, 2009, 'summerAttn', 'HS', 'coreClass')
GO
SELECT * FROM dbo.Course
SELECT * FROM dbo.Course_Prefs
GO
IF EXISTS (SELECT * FROM sys.views WHERE name = 'Loc_uc_Info')
DROP VIEW dbo.Loc_uc_Info
GO
CREATE VIEW dbo.Loc_uc_Info AS
SELECT
cp.crs_nbr,
c.abbr,
cp.fiscal_yr,
case when isnumeric(cp.item_name) = 1 then convert(int,cp.item_name) else null end AS loc_id,
--convert(int, cp.item_name) as loc_id,
substring(cp.value, 1, 1) as subject_area,
substring(cp.value, 3, 1) as honors,
substring(cp.value, 5, 1) as can_be_elective
FROM dbo.Course_Prefs AS cp
JOIN dbo.Course AS c ON cp.crs_nbr = c.crs_nbr
--WHERE cp.group_name = 'LocUCInfo'
GO
SELECT * FROM dbo.Loc_uc_Info
GO
Results
ID Abbr Crs_Nbr
----------- ----- -----------
1 Crs1 5327
2 Crs2 5329
3 Crs3 2000
4 Crs4 15746
5 Crs5 12367
ID Crs_Nbr Fiscal_Yr Group_Name Item_Name Value
----------- ----------- ----------- -------------------------------------------------- -------------------------------------------------- ----------------------------------------------------------------------------------------------------
1 5327 2007 StuAchievement Qualifier alg
2 5329 2007 StuAchievement Qualifier alg
153 2000 2003 LocUCInfo 543 F,0,0
154 2000 2003 LocUCInfo 542 F,0,0
6149 15746 2009 summerAttn HS coreClass
6150 12367 2009 summerAttn HS coreClass
crs_nbr abbr fiscal_yr loc_id subject_area honors can_be_elective
----------- ----- ----------- ----------- ------------ ------ ---------------
5327 Crs1 2007 NULL a g
5329 Crs2 2007 NULL a g
2000 Crs3 2003 543 F 0 0
2000 Crs3 2003 542 F 0 0
15746 Crs4 2009 NULL c r C
12367 Crs5 2009 NULL c r C
Edit: Forgot to include Penfold's code.
Final Working Code Based on HLGEM's Suggestion
IF EXISTS (SELECT * FROM sys.tables WHERE name = 'Course')
DROP TABLE dbo.Course
GO
CREATE TABLE dbo.Course (
ID int not null, -- identity
Abbr varchar(5) not null,
Crs_Nbr int not null --references course (crs_nbr)
)
GO
IF EXISTS (SELECT * FROM sys.tables WHERE name = 'Course_Prefs')
DROP TABLE dbo.Course_Prefs
GO
CREATE TABLE dbo.Course_Prefs (
ID int not null, -- identity
Crs_Nbr int not null, --references course (crs_nbr)
Fiscal_Yr int not null,
Group_Name varchar(50) not null,
Item_Name varchar(50) null,
Value varchar(100) not null
)
GO
INSERT INTO dbo.Course VALUES (1, 'Crs1', 5327)
INSERT INTO dbo.Course VALUES (2, 'Crs2', 5329)
INSERT INTO dbo.Course VALUES (3, 'Crs3', 2000)
INSERT INTO dbo.Course VALUES (4, 'Crs4', 15746)
INSERT INTO dbo.Course VALUES (5, 'Crs5', 12367)
GO
INSERT INTO dbo.Course_Prefs VALUES (1, 5327, 2007, 'StuAchievement', 'Qualifier', 'alg')
INSERT INTO dbo.Course_Prefs VALUES (2, 5329, 2007, 'StuAchievement', 'Qualifier', 'alg')
INSERT INTO dbo.Course_Prefs VALUES (153, 2000, 2003, 'LocUCInfo', '543', 'F,0,0')
INSERT INTO dbo.Course_Prefs VALUES (154, 2000, 2003, 'LocUCInfo', '542', 'F,0,0')
INSERT INTO dbo.Course_Prefs VALUES (6149, 15746, 2009, 'summerAttn', 'HS', 'coreClass')
INSERT INTO dbo.Course_Prefs VALUES (6150, 12367, 2009, 'summerAttn', 'HS', 'coreClass')
GO
SELECT * FROM dbo.Course
SELECT * FROM dbo.Course_Prefs
GO
IF EXISTS (SELECT * FROM sys.views WHERE name = 'Loc_uc_Info')
DROP VIEW dbo.Loc_uc_Info
GO
CREATE VIEW dbo.Loc_uc_Info AS
SELECT
cp.crs_nbr,
c.abbr,
cp.fiscal_yr,
convert(int,
case
when isnumeric(cp.item_name) = 1 then cp.item_name
else 0
end
) as loc_id,
substring(cp.value, 1, 1) as subject_area,
substring(cp.value, 3, 1) as honors,
substring(cp.value, 5, 1) as can_be_elective
FROM dbo.Course_Prefs AS cp
JOIN dbo.Course AS c ON cp.crs_nbr = c.crs_nbr
WHERE cp.group_name = 'LocUCInfo'
GO
SELECT * FROM dbo.Loc_uc_Info
GO
Just from the top of my head: What about creating two views?
One that does the join without converting and another one that just does the conversion on the first view.
Since the first view should only contain numbers in the Item_Name (namely 543 and 542) you will not have the conversion error.