SQL Server - Inserting default values bcp - sql-server

In SQL Server, how to insert default values while using bcp command?
Scenario is from the below table, while running bcp command, column 'sno' is identity column, where values should increment automatically by 1, data for values column should come from datafile and values for date column should automatically updated to today's date and values for status column should updated as Flag1.
For normal usage I know how to create bcp format file. For the above scenario, how can I create a format file and insert data to table1?
Table format:
CREATE TABLE [dbo].[table1]
(
SNo int IDENTITY(1,1) NOT NULL,
values varchar(13) NOT NULL,
date datetime NOT NULL,
status varchar(50)
)
Table1:
sno | values | date | status
-----+----------+------------+--------
1 | 111111 | 2015-08-17 | Flag1
2 | 222222 | 2015-08-17 | Flag1

Basically, you just need to put 0 as the host column number to avoid a column from being inserted by bcp.
So assuming you have a default constraint for your [date] column:
ALTER TABLE dbo.table1
ADD CONSTRAINT DF_Table1_Date DEFAULT(SYSDATETIME()) FOR [Date]
and somehow you have also set up some way to calculate the [status] - then you could use this format file:
12.0
4
1 SQLCHAR 0 12 ";" 0 SNo ""
2 SQLCHAR 0 13 ";" 2 values SQL_Latin1_General_CP1_CI_AS
3 SQLDATETIME 0 24 ";" 0 date ""
4 SQLCHAR 0 50 "\r\n" 0 status SQL_Latin1_General_CP1_CI_AS
and thus you would be really only importing the [values] column - the SNo is automatically set by SQL Server (identity column), the [date] column is automatically set to the current date&time by means of the default constraint - now you'll have to find a way to fill in the [status] column upon or after insert!

Related

Bulk Insert Czech characters

I got a tab delimited text file (cznames.txt)with PersonID and Names with Czech characters in it.
I am figuring out how to load it SQL Server table. Here's what I did
USE myDatabase
Go
CREATE TABLE [dbo].[myNameTable](
[ID] smallint NOT NULL,
[NAME] [nvarchar](50) collate Czech_CI_AS
) ON [PRIMARY]
I then created format file
bcp myDatabase.dbo.myNameTable format nul -c -f "C\temp\Czech.fmt" -T -Smyserver -Umyuser -P1mypwd
I used the below statement to insert into table
BULK INSERT myDatabase.dbo.myNameTable FROM 'C:\temp\cznames.txt'
WITH (FormatFile = 'C:\temp\Czech.fmt', FIRSTROW = 2, ROWTERMINATOR = '0X0A');
I find no errors but the characters in the table look very different from the text file.
Sample cznames.txt
ID NAME
1 Vysočina
2 Olomoucký
3 Středočeský
4 Hlavní město
Here's the format file
10.0
2
1 SQLCHAR 0 7 "\t" 1 ID ""
2 SQLCHAR 0 100 "\r\n" 2 Region Czech_CI_AS
Can anyone help me
Thanks
Please try the following solution.
The CODEPAGE = '65001' setting was a remedy.
Versions prior to SQL Server 2016 (13.x) don't support code page 65001
(UTF-8 encoding).
And without a format file.
SQL
USE tempdb;
GO
DROP TABLE IF EXISTS dbo.myNameTable;
CREATE TABLE dbo.myNameTable(
ID INT NOT NULL,
NAME NVARCHAR(50) collate Czech_CI_AS
);
BULK INSERT dbo.myNameTable
FROM 'e:\Temp\cznames.txt'
WITH
(
FIRSTROW = 2,
FIELDTERMINATOR = '\t',
ROWTERMINATOR = '0x0A',
CODEPAGE = '65001'
);
SELECT * FROM dbo.myNameTable;
Output
+----+--------------+
| ID | NAME |
+----+--------------+
| 1 | Vysočina |
| 2 | Olomoucký |
| 3 | Středočeský |
| 4 | Hlavní město |
+----+--------------+

SQL Server Merge Update With Partial Sources

I have a target table for which partial data arrives at different times from 2 departments. The keys they use are the same, but the fields they provide are different. Most of the rows they provide have common keys, but there are some rows that are unique to each department. My question is about the fields, not the rows:
Scenario
the target table has a key and 30 fields.
Dept. 1 provides fields 1-20
Dept. 2 provides fields 21-30
Suppose I loaded Q1 data from Dept. 1, and that created new rows 100-199 and populated fields 1-20. Later, I receive Q1 data from Dept. 2. Can I execute the same merge code I previously used for Dept. 1 to update rows 100-199 and populate fields 21-30 without unintentionally changing fields 1-20? Alternatively, would I have to tailor separate merge code for each Dept.?
In other words, does (or can) "Merge / Update" operate only on target fields that are present in the source table while ignoring target fields that are NOT present in the source table? In this way, Dept. 1 fields would NOT be modified when merging Dept. 2, or vice-versa, in the event I get subsequent corrections to this data from either Dept.
You can use a merge instruction, where you define a source and a target data, and what happens when a registry is found on both, just on the source, just on the target, and even expand it with custom logic, like it's just on the source, and it's older than X, or it's from department Y.
-- I'm skipping the fields 2-20 and 22-30, just to make this shorter.
create table #target (
id int primary key,
field1 varchar(100), -- and so on until 20
field21 varchar(100), -- and so on until 30
)
create table #dept1 (
id int primary key,
field1 varchar(100)
)
create table #dept2 (
id int primary key,
field21 varchar(100)
)
/*
Creates some data to merge into the target.
The expected result is:
| id | field1 | field21 |
| - | - | - |
| 1 | dept1: 1 | dept2: 1 |
| 2 | | dept2: 2 |
| 3 | dept1: 3 | |
| 4 | dept1: 4 | dept2: 4 |
| 5 | | dept2: 5 |
*/
insert into #dept1 values
(1,'dept1: 1'),
--(2,'dept1: 2'),
(3,'dept1: 3'),
(4,'dept1: 4')
insert into #dept2 values
(1,'dept2: 1'),
(2,'dept2: 2'),
--(3,'dept2: 3'),
(4,'dept2: 4'),
(5,'dept2: 5')
-- Inserts the data from the first department. This could be also a merge, it necessary.
insert into #target(id, field1)
select id, field1 from #dept1
merge into #target t
using (select id, field21 from #dept2) as source_data(id, field21)
on (source_data.id = t.id)
when matched then update set field21=source_data.field21
when not matched by source and t.field21 is not null then delete -- you can even use merge to remove some records that match your criteria
when not matched by target then insert (id, field21) values (source_data.id, source_data.field21); -- Every merge statement should end with ;
select * from #target
You can see this code running on this DB Fiddle

Update a Column in All Rows with Different Values from an expression based on a parameter from another Column in the Current Row

Please, I had recently changed my DB from MS Access To SQL server Express, Access is a wonderful small scale DB for a SINGLE user that have a very simple VBA Functionality which I missed in SQL server!
In My Old Access DB I have [Account] table with a Sub Procedure that Update a field in All Rows in a table with the result of this Expression:
[SortOrder] = [AccountNumber] * (10 ^ (Len(MaximumAccountNumber) - Len([AccountNumber])))
where MaximumAccountNumber is a Variable represent the Max AccountNumber in the table.
I was searching for a solution for many days but no one example can give me an idea for how to use a Value from a column in the SAME Row to Calculate the result for another Column in that Row and so on for All the Rows in the table as if in the Following VBA code:
Do while Not rst.EOF
rst.Edit
rst![Field1] = rst![Field2] * ( 10 ^ ( (Len(MaximumAccountNumber) - Len(rst![Field2]) ) )
rst.Update
rst.MoveNext
Loop
Please How to implement such an Update efficiently In SQL server T-SQL without using a Cursor because the Rows Count in the table could reaches to > 100,000?
Please, I want to do This by Creating a SP which I Can Fire it (Trigger) after every Insert of a New Account to Re-Calculate the SortOrder of All the Rows in the table as in the Following:
CREATE PROCEDURE [dbo].[SortingOrder]
#MaxOrder Numeric(38,0) = 0,
#Digits int = 0,
As
BEGIN
set #MaxOrder = (select MAX([AccNumber]) from Account)
set #Digits = (select LEN(#MaxOrder))
Update dbo.Account
Set [SortOrder] = (Select ([AccNumber] * (POWER(10 ,(#Digits -
LEN([AccNumber]))) from [Account] )
END
GO
As in This Sample Table [Account]:
AccID AccNumber SortOrder
----- --------- ---------
023 23 2300
054 243 2430
153 5434 5434
But when Insert a new Record, I want the SortOrder to be Updated for All the rows to a Number with the same Numbers Count based on 10 Power(Length of the Max AccNumber) as in the Following:
AccID AccNumber SortOrder
----- --------- ---------
023 23 230000000
054 243 243553000
153 5434 543400000
233 432345625 432345625
Try this:
Table Schema:
CREATE TABLE Account(AccID INT,AccNumber BIGINT,SortOrder BIGINT)
INSERT INTO Account VALUES(23,23,23)
INSERT INTO Account VALUES(54,254,254)
INSERT INTO Account VALUES(125,25487,25487)
T-SQL Query:
DECLARE #MaxValLen INT
SELECT #MaxValLen = LEN(MAX(AccNumber)) FROM Account
UPDATE Account
SET SortOrder = AccNumber * POWER(10,#MaxValLen - LEN(AccNumber))
Output:
| AccID | AccNumber | SortOrder |
|-------|-----------|-----------|
| 23 | 23 | 23000 |
| 54 | 254 | 25400 |
| 125 | 25487 | 25487 |

SQL IF/ CASE statement

I am fairly new to SQL and I can't figure out what to do here.
I have a database of financial data with one column being [Days]. I need to add a new column into it which will add a category in which the number of days fall into (0, 1-30, 30-60 etc).
In excel this would look like this =IF(A1>90,"90-120",IF(A1>60,"60-90".......)
The final database should look like this:
Days | Category
29 | 0-30
91 | 90-120
0 | 0
.
.
.
Thx in advance
You can use case:
select days,
(case when days > 90 then '90-120' -- should this be >= ?
when days > 60 then '60-90' -- should this be >= ?
. . .
end) as Category
from t;
Complete SQL:
select Days,
(case when days > '90' then '91-120'
when days > '60' then '61-90'
when days > '30' then '31-60'
when days > '0' then '1-30' else '0' end
end) as Category
from t;
Here is another way using IIF if you are using SQL Server 2012+:
CREATE TABLE Numbers (Number INT );
INSERT INTO Numbers VALUES
(1),
(0),
(15),
(29),
(32),
(54),
(59),
(60),
(63),
(89),
(90),
(140);
SELECT IIF(Number BETWEEN 90 AND 120, '90-120',
IIF(Number BETWEEN 60 AND 89, '60-90',
IIF(Number BETWEEN 30 AND 59 , '30-60' ,
IIF(Number BETWEEN 1 AND 29, '1-30' ,
IIF(Number = 0, '0', 'OutRange'))))) AS Category
FROM Numbers;
try this
create table #tmp ([Days] int)
insert into #tmp values (29)
insert into #tmp values (91)
insert into #tmp values (0)
insert into #tmp values (65)
SELECT
CASE WHEN [Days]=0 then CONVERT(VARCHAR(15),0)
ELSE CONVERT(VARCHAR(15),[Days]/30*30)+'-'+ CONVERT(VARCHAR(15),([Days]/30*30)+30) END AS Category
from #tmp
drop table #tmp
select *,number/30, ltrim(number/30*30)+'-' +ltrim((number/30+1)*30) from #Numbers
+--------+---+-------+
| Number | | |
+--------+---+-------+
| 1 | 0 | 0-30 |
| 0 | 0 | 0-30 |
| 15 | 0 | 0-30 |
| 29 | 0 | 0-30 |
| 32 | 1 | 30-60 |
| 54 | 1 | 30-60 |
| 59 | 1 | 30-60 |
| 60 | 2 | 60-90 |
| 63 | 2 | 60-90 |
+--------+---+-------+
One solution to your dilemma may be to insert a new database column that uses a SQL Server feature known as a "Computed Column Specification" into your table.
A Computed Column Specification is a method whereby a database column's value can be calculated when the row is updated. That value can be optionally also be persisted in the database so that when it is queried no calculation has to be performed at that time (just on the INSERT).
I like this solution because you don't have to do any special calculations upon querying the data. You'll pull the new column data with a simple SELECT.
You didn't list specifics, so let's suppose that your database table is named [FinancialData], and that it has defined in it a column named [Days] that is of some numeric type (int, smallint, tinyint, decimal, float, money, numeric, or real).
You can add the computed column as follows:
ALTER TABLE [FinancialData] ADD
Category AS (CASE WHEN [Days] >= 90 THEN '90-120'
WHEN [Days] >= 60 THEN '60-90'
WHEN [Days] >= 30 THEN '30-60'
WHEN [Days] >= 1 THEN '1-30'
WHEN [Days] = 0 THEN '0'
END) PERSISTED;
Note the word "PERSISTED" in the SQL statement above. This is what causes the database table to actually store the calculated value in the database table when the [Days] column is inserted or changed. If you don't want to store the value, simply leave out the word "PERSISTED".
When the computed column is added to the table by executing the SQL statement above, values will be computed and stored for all existing rows in the table. When inserting a new row into the table, do not supply a value for the new [Category] column. This is because a) it won't work, and b) that column's value will be computed from the [Days] column value.
To retrieve data from the new column, you simply list that column in the SELECT statement (or use *):
SELECT [Days], [Category]
FROM [FinancialData];
A couple of caveats to note: 1) This is SQL Server specific. Most other database engines have no support for this feature. 2) You didn't state whether the [Days] column is nullable - if so, this solution will have to be modified to support that.

Auto running number ID with format xxxx/year number (9999/12) in SQL Server stored procedure

I have one table (Stock_ID, Stock_Name). I want to write a stored procedure in SQL Server with Stock_ID running number with a format like xxxx/12 (xxxx = number start from 0001 to 9999; 12 is the last 2 digits of current year).
My scenario is that if the year change, the running number will be reset to 0001/13.
what do you intend to do when you hit more than 9999 in a single year??? it may sound impossible, but I've had to deal with so many "it will never happen" data related design mess-ups over the years from code first design later developers. These are major pains depending on how may places you need to fix these items which are usually primary key and foreign keys used all over.
This looks like a system requirement to SHOW the data this way, but it is the developers responsibility to design the internals of the application. The way you store it and display it don't need to be identical. I'd split that into two columns, using an int for the number portion and a tiny int for the 2 digit year portion. You can use a computed column for quick and easy display (persist it and index if necessary), where you pad with leading zeros and add the slash. Throw in a check constraint on the year portion to make sure it stays within a reasonable range. You can make the number portion an identity and just have a job reseed it back to 1 every new years eve.
try it out:
--drop table YourTable
--create the basic table
CREATE TABLE YourTable
(YourNumber int identity(1,1) not null
,YourYear tinyint not null
,YourData varchar(10)
,CHECK (YourYear>=12 and YourYear<=25) --optional check constraint
)
--add the persisted computed column
ALTER TABLE YourTable ADD YourFormattedNumber AS ISNULL(RIGHT('0000'+CONVERT(varchar(10),YourNumber),4)+'/'+RIGHT(CONVERT(varchar(10),YourYear),2),'/') PERSISTED
--make the persisted computed column the primary key
ALTER TABLE YourTable ADD CONSTRAINT PK_YourTable PRIMARY KEY CLUSTERED (YourFormattedNumber)
sample data:
--insert rows in 2012
insert into YourTable values (12,'aaaa')
insert into YourTable values (12,'bbbb')
insert into YourTable values (12,'cccc')
--new years eve job run this
DBCC CHECKIDENT (YourTable, RESEED, 0)
--insert rows in 2013
insert into YourTable values (13,'aaaa')
insert into YourTable values (13,'bbbb')
select * from YourTable order by YourYear,YourNumber
OUTPUT:
YourNumber YourYear YourData YourFormattedNumber
----------- -------- ---------- -------------------
1 12 aaaa 0001/12
2 12 bbbb 0002/12
3 12 cccc 0003/12
1 13 aaaa 0001/13
2 13 bbbb 0002/13
(5 row(s) affected)
to handle the possibility of more than 9999 rows per year try a different computed column calculation:
CREATE TABLE YourTable
(YourNumber int identity(9998,1) not null --<<<notice the identity starting point, so it hits 9999 quicker for this simple test
,YourYear tinyint not null
,YourData varchar(10)
)
--handles more than 9999 values per year
ALTER TABLE YourTable ADD YourFormattedNumber AS ISNULL(RIGHT(REPLICATE('0',CASE WHEN LEN(CONVERT(varchar(10),YourNumber))<4 THEN 4 ELSE 1 END)+CONVERT(varchar(10),YourNumber),CASE WHEN LEN(CONVERT(varchar(10),YourNumber))<4 THEN 4 ELSE LEN(CONVERT(varchar(10),YourNumber)) END)+'/'+RIGHT(CONVERT(varchar(10),YourYear),2),'/') PERSISTED
ALTER TABLE YourTable ADD CONSTRAINT PK_YourTable PRIMARY KEY CLUSTERED (YourFormattedNumber)
sample data:
insert into YourTable values (12,'aaaa')
insert into YourTable values (12,'bbbb')
insert into YourTable values (12,'cccc')
DBCC CHECKIDENT (YourTable, RESEED, 0) --new years eve job run this
insert into YourTable values (13,'aaaa')
insert into YourTable values (13,'bbbb')
select * from YourTable order by YourYear,YourNumber
OUTPUT:
YourNumber YourYear YourData YourFormattedNumber
----------- -------- ---------- --------------------
9998 12 aaaa 9998/12
9999 12 bbbb 9999/12
10000 12 cccc 10000/12
1 13 aaaa 0001/13
2 13 bbbb 0002/13
(5 row(s) affected)
This might help:
DECLARE #tbl TABLE(Stock_ID INT,Stock_Name VARCHAR(100))
INSERT INTO #tbl
SELECT 1,'Test'
UNION ALL
SELECT 2,'Test2'
DECLARE #ShortDate VARCHAR(2)=RIGHT(CAST(YEAR(GETDATE()) AS VARCHAR(4)),2)
;WITH CTE AS
(
SELECT
CAST(ROW_NUMBER() OVER(ORDER BY tbl.Stock_ID) AS VARCHAR(4)) AS RowNbr,
tbl.Stock_ID,
tbl.Stock_Name
FROM
#tbl AS tbl
)
SELECT
REPLICATE('0', 4-LEN(RowNbr))+CTE.RowNbr+'/'+#ShortDate AS YourColumn,
CTE.Stock_ID,
CTE.Stock_Name
FROM
CTE
From memory, this is a way to get the next id:
declare #maxid int
select #maxid = 0
-- if it does not have #maxid will be 0, if it was it will give the next id
select #maxid = max(convert(int, substring(Stock_Id, 1, 4))) + 1
from table
where substring(Stock_Id, 6, 2) = substring(YEAR(getdate()), 3, 2)
declare #nextid varchar(7)
select #nextid = right('0000'+ convert(varchar,#maxid),4)) + '/' + substring(YEAR(getdate()), 3, 2)

Resources