要插入的存储过程不起作用:将数据类型nvarchar转换为数值时出错

时间:2018-12-04 02:45:50

标签: sql sql-server

我正在尝试使用存储过程进行插入,但是我得到了

  

将数据类型nvarchar转换为数字时出错

我正在使用SQL Server / Azure Data Studio。

这是我正在使用的桌子

CREATE TABLE Clinics 
(
    ClinicID INT PRIMARY KEY IDENTITY(1, 1),
    ClinicName NVARCHAR(100) NOT NULL UNIQUE,
    ClinicPhoneNumber NVARCHAR(100) NOT NULL,
    ClinicAddress NVARCHAR(100) NOT NULL,
    ClinicCity NVARCHAR(100) NOT NULL,
    ClinicState NVARCHAR(100) NOT NULL,
    ClinicZipCode NVARCHAR(10),

    CONSTRAINT CHK_ClinicPhone 
        CHECK(ClinicPhoneNumber = FORMAT(CAST(ClinicPhoneNumber AS NUMERIC), '###-###-####')),
    CONSTRAINT CHK_ClinicZip 
        CHECK(ClinicZipCode = FORMAT(CAST(ClinicZipCode AS NUMERIC), '#####') 
        OR ClinicZipCode = FORMAT(CAST(ClinicZipCode AS NUMERIC), '#####-####'))
);
GO

这是我的程序代码

CREATE PROCEDURE pInsertClinic
    (@ClinicName NVARCHAR(100),
     @ClinicPhoneNumber NVARCHAR(100),
     @ClinicAddress NVARCHAR(100),
     @ClinicCity NVARCHAR(100),
     @ClinicState NVARCHAR(100),
     @ClinicZipCode NVARCHAR(10))
AS
BEGIN
    DECLARE @RC int = 0;
    BEGIN TRY
    BEGIN TRANSACTION 
        SET NOCOUNT ON 

        INSERT INTO Clinics (ClinicName, ClinicPhoneNumber, ClinicAddress, 
                             ClinicCity, ClinicState, ClinicZipCode)
        VALUES (@ClinicName, @ClinicPhoneNumber, @ClinicAddress,
                @ClinicCity, @ClinicState, @ClinicZipCode)

        COMMIT TRANSACTION
        SET @RC = +1
    END TRY
    BEGIN CATCH
        IF (@@Trancount > 0) 
            ROLLBACK TRANSACTION
        PRINT Error_Message()
        SET @RC = -1
    END CATCH

    RETURN @RC;
END
GO

这是我的执行代码:

BEGIN 
    EXEC pInsertClinic
        @ClinicName = 'General Clinic',
        @ClinicPhoneNumber = '943-309-3094',
        @ClinicAddress = '9876 fourth st',
        @ClinicCity = 'Seattle',
        @ClinicState = 'WA',
        @ClinicZipCode = '98118';
    SELECT * FROM vClinics;
END
GO

姓名,电话和地址信息应与执行程序中的相同。诊所ID应该自动分配为整数

2 个答案:

答案 0 :(得分:0)

表结构应该是这样的。

CREATE TABLE Clinics 
(
    ClinicID INT PRIMARY KEY IDENTITY(1, 1),
    ClinicName NVARCHAR(100) NOT NULL UNIQUE,
    ClinicPhoneNumber NVARCHAR(100) NOT NULL,
    ClinicAddress NVARCHAR(100) NOT NULL,
    ClinicCity NVARCHAR(100) NOT NULL,
    ClinicState NVARCHAR(100) NOT NULL,
    ClinicZipCode NVARCHAR(10),

    CONSTRAINT CHK_ClinicPhone 
         CHECK(ClinicPhoneNumber = FORMAT(CAST(REPLACE(ClinicPhoneNumber,'-','') AS NUMERIC), '###-###-####')),
    CONSTRAINT CHK_ClinicZip 
         CHECK(ClinicZipCode = FORMAT(CAST(ClinicZipCode AS NUMERIC), '#####') 
            OR ClinicZipCode = FORMAT(CAST(ClinicZipCode AS NUMERIC), '#####-####'))
 );
 GO

答案 1 :(得分:0)

重新开始。这次,在执行其他任何操作之前,请先考虑一下数据类型。问题:

  • 邮政编码为空。为什么?约束如果有效,将不允许它。以及为什么使用nvarchar?您只打算存储数字字符。
  • 停止使用疯狂的命名约定。给每个列名加上表名只会增加每个人编写查询所需的工作量。
  • 电话号码是nvarchar(100)。为什么?约束如果有效,将不允许它。以及为什么使用nvarchar?您只打算存储数字字符。
  • 状态为nvarchar(100)。为什么?您打算存储状态缩写。有时复制/粘贴不是您的朋友。以及为什么使用nvarchar?
  • 没有理由启动包含单个DML语句的事务。
  • 请勿使用“打印”将错误传达给呼叫者。打印将生成一个结果集-需要编写调用应用程序以询问该结果集,以了解是否发生了错误(以及错误类型)。要么重新抛出错误,然后让调用者抓住它,而无需尝试/捕获。 Erland对error handling进行了全面的讨论-可能在您的头上,但是请保留链接作为参考(他还讨论了其他常见但复杂的问题)。
  • 测试脚本中的最后一条语句是从vClinics中选择的。您只是没有注意。如果有视图(大概是基于名称),则没有在DDL中提供它。如果您没有意见,那么...谁知道。

对此以及您可能遇到的任何其他问题(或可能遇到的问题),您的第一个响应应该是搜索互联网。强制执行有关字符串内容的规则是一个常见问题,您可以通过非常简单的搜索找到许多示例。在这种情况下,您可以简单地搜索“ stackoverflow限制电话号码”以查找过去的讨论。因此,依靠one of those,我们可以使用类似以下的内容。

set nocount on; 
if object_id('tempdb..#Clinics') is not null 
drop table #Clinics;
go

CREATE TABLE #Clinics 
(
    ID INT PRIMARY KEY IDENTITY(1, 1),
    Name NVARCHAR(100) NOT NULL UNIQUE,
    PhoneNumber VARCHAR(12) NOT NULL,
    Address NVARCHAR(100) NOT NULL,
    City NVARCHAR(100) NOT NULL,
    State CHAR(2) NOT NULL,
    ZipCode VARCHAR(10) not null 
);
GO

insert #Clinics (Name, PhoneNumber, Address, City, State, ZipCode) 
values ('Text Clinic', '111-2222', '123 Main Street', 'Seattle', 'WA', '11111');
select * from #Clinics; 
go

alter table #Clinics add constraint chk_phone check (PhoneNumber like '[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]') 
go

insert #Clinics (Name, PhoneNumber, Address, City, State, ZipCode) 
values ('Bad number', '1112222', '987 Main Street', 'Seattle', 'WA', '11111'); -- error
insert #Clinics (Name, PhoneNumber, Address, City, State, ZipCode) 
values ('Good Clinic', '123-4444', '987 Main Street', 'Seattle', 'WA', '11111');
insert #Clinics (Name, PhoneNumber, Address, City, State, ZipCode) 
values ('Short Number', '123-444', '987 Main Street', 'Seattle', 'WA', '11111'); -- error
insert #Clinics (Name, PhoneNumber, Address, City, State, ZipCode) 
values ('Short Exchange', '13-4445', '987 Main Street', 'Seattle', 'WA', '11111'); --error
insert #Clinics (Name, PhoneNumber, Address, City, State, ZipCode) 
values ('Bad Separator', '213+4445', '987 Main Street', 'Seattle', 'WA', '11111'); --error
insert #Clinics (Name, PhoneNumber, Address, City, State, ZipCode) 
values ('Letters', '213-A445', '987 Main Street', 'Seattle', 'WA', '11111'); --error
select * from #Clinics;

这显示了方法-您需要扩展它才能完整地实施。但是在您这样做之前,我必须质疑原因。通常可以在GUI中更好地实现这种功能。通常,人们会使用一个会自动强制执行此操作的掩码。应用程序还可以为基本输入错误提供更好的指导和错误处理-丑陋且难以读取的本机sql server错误几乎不友好。

您还应该仔细考虑重新考虑某种格式对存储的强制使用。有些人更喜欢将电话号码视为(999)123-4567。我已经看到了许多使用句点而不是破折号的示例。您在此作出的承诺以后将很难更改。

最后一条评论。您不能一次完成所有操作。暂时不使用该过程。定义表并使用包含好数据和坏数据的DML语句验证约束是否正常工作。一旦可行,您就可以添加存储过程的复杂性。一旦可行,就可以将过程的使用合并到应用程序中。该方法适用于任何类型的代码开发-您不能在未经测试和验证的基础上构建。