How to write query with cast and switch functions in SQL Server? - sql-server

I have to convert an int column to a text column and replace the integer values within that column.
For example I have a column status that can contain values like 0, 1, 2, 3, 4, 5, 6, 7.
For '0' I have to replace the value with "New", for '1' with "Identified" and so on.
For this scenario how to write a SQL query?

Personally, I'd go with a mapping table, but another option is CHOOSE() or even the traditional CASE
Note the +1 in the CHOOSE option ... 0 is not a valid option and would return NULL
Example
Declare #YourTable Table ([Status] int)
Insert Into #YourTable Values
(0),(1),(2),(3)
Select *
,ViaCHOOSE = choose([Status]+1,'New','Identified','Some Other','...')
,ViaCASE = case [Status]
when 0 then 'New'
when 1 then 'Identified'
when 2 then 'Some Other'
else null -- or 'Undefined'
end
From #YourTable
Results
Status ViaCHOOSE ViaCASE
0 New New
1 Identified Identified
2 Some Other Some Other
3 ... ...

You could create a (temporary) table with that mapping.
create table XYMapping (number int, text varchar(max));
INSERT INTO XYMapping (number, text) VALUES (1, 'New'), (2, 'Identified'); -- ...
Insert all values and then join them.

Steps to follow to convert int column to text and replace the existing values.
Alter the table. Since INT converts to text implicitly NOT NULL is able to be maintained
Exec UPDATE statement using conversion table specified using VALUES table value constructor
Something like this
drop table if exists #samples;
go
create table #samples (
id int not null,
stat varchar(10) not null);
insert #samples(id, stat) values
(10, 'Start'),
(1, 'Start'),
(1, 'Failed'),
(2, 'Start'),
(3, 'Start'),
(3, 'Failed'),
(4, 'Start'),
(4, 'Failed'),
(4, 'Start'),
(4, 'Failed');
/* step 1: alter the table (and implicitly convert) */
alter table #samples
alter column
id varchar(20) not null;
/* step 2: update based on conversion table */
update s
set id=v.new_str
from #samples s
join
(values ('1', 'New'),
('2', 'Identified'),
('3', 'Identified'),
('4', 'Identified'),
('10', 'Other')) v(old_int, new_str) on s.id=v.old_int;
select * from #samples;
id stat
Other Start
New Start
New Failed
Identified Start
Identified Start
Identified Failed
Identified Start
Identified Failed
Identified Start
Identified Failed

Related

Why can't I find a table in a database?

USE mydb;
create table #payments
( id int, contract_ID varchar(20), payment_number int, payment_status varchar(20));
insert #payments (id, contract_id, payment_number, payment_status)
values
(1, 'contract1', 1, 'Processed'),
(2, 'contract1', 2, 'Received'),
(3, 'contract2', 1, 'Processed'),
(4, 'contract2', 2, 'Approved'),
(5, 'contract1', 3, 'Approved'),
(6, 'contract2', 3, 'Processed');
Is it correct that the above SQL script creates a table named #payments in the database 'mydb'?
When I run the above for the second time, SSMS says
There is already an object named '#payments' in the database.
But why can't I find the table in the output of
USE mydb;
SELECT TABLE_NAME FROM INFORMATION_SCHEMA.COLUMNS
GROUP BY TABLE_NAME
?
Thanks
Is it correct that the above SQL script creates a table named #payments in the database 'mydb'?
No, that's not correct.
# indicates a temporary table, it's created in tempdb
select Object_Id('tempdb.dbo.#payments');
select *
from tempdb.sys.tables
where name like '#payments%';
You'll see it exists with a negative object ID and name appended with a long "uniquifier".
Likewise prefixing any object name with # indicates it is a temporary object and will be managed for you in tempdb. Objects created with a single hash prefix are scoped to your own session, a double hash indicates they are global and visible to all sessions. Temporary objects are automatically dropped when they go out of scope, ie, are no longer referenced.
IN MSSQL when we create temp table we use #
use mydb;
create table #payments
( id int, contract_ID varchar(20), payment_number int, payment_status varchar(20));
insert into #payments (id, contract_id, payment_number, payment_status)
values
(1, 'contract1', 1, 'Processed'),
(2, 'contract1', 2, 'Received'),
(3, 'contract2', 1, 'Processed'),
(4, 'contract2', 2, 'Approved'),
(5, 'contract1', 3, 'Approved'),
(6, 'contract2', 3, 'Processed');
select * from #payments
You can find table by using below query:
select * from tempdb..sysobjects

How to create a T-SQL function to read values which have been updated as part of the same [update] transaction

I need to use a function in an update of several records.
How to make a T-SQL function read values which have been updated during that update?
Below I created a simple example demonstrating what I get.
My example: table has Category column and I build code based on a category. Function finds the last one withing the category + 1. Or 10000 if it is the first record.
It would work just fine if update is a single record, but updates all records with the same value otherwise.
Below is working code:
CREATE TABLE Tbl
(
Id INT NOT NULL PRIMARY KEY,
Category CHAR(1) NOT NULL,
Code INT NULL
)
CREATE FUNCTION dbo.GetNextAvailableCode
(#Category CHAR(1))
RETURNS INT
AS
BEGIN
DECLARE #MaxCode INT
SELECT #MaxCode = MAX(Code)
FROM dbo.Tbl
WHERE Category = #Category
SET #MaxCode = ISNULL(#MaxCode + 1, 10000);
RETURN #MaxCode
END;
GO
INSERT INTO Tbl (Id, Category)
VALUES (1, 'A'), (2, 'A'), (3, 'A'),
(4, 'B'), (5, 'B'),
(6, 'C'), (7, 'C'), (8, 'C'), (9, 'C')
SELECT * FROM Tbl
UPDATE dbo.Tbl
SET Code = dbo.GetNextAvailableCode(Category)
WHERE Code IS NULL
SELECT * FROM Tbl
GO
DROP TABLE Tbl;
DROP FUNCTION dbo.GetNextAvailableCode;
Here is the result I'm getting:
What I'd like to get is the following:
But it's possible only if next function call can see already changed values...
Any idea how to implement such thing?
Since it is not possible to use function to achieve the desired effect, I re-wrote the update to be used without function and it worked!
Here is the update which produces the result I need:
;WITH data AS (
SELECT Id, Category, Code, ROW_NUMBER() OVER (PARTITION BY Category ORDER BY Id) AS RowNo
FROM dbo.Tbl
WHERE Code IS NULL
)
, maxData AS (
SELECT Category, MAX(Code) AS MaxCode
FROM dbo.Tbl
GROUP BY Category
)
UPDATE S
SET Code = ISNULL(T.MaxCode, 10000) + S.RowNo
FROM data S JOIN maxData T ON T.Category = S.Category
WHERE S.Code IS NULL
Result:

Get max value from table and group by with column having comma separated value with different order or having more values

I am having a table like this along with data
CREATE TABLE temp (
`name` varchar(20),
`ids` varchar(20),
`value1` int,
`value2` int
);
INSERT INTO temp(`name`,`ids`, `value1`, `value2`) values
('A', '1,2', 10, 11),
('A', '2,1', 12, 100),
('A', '1,2,3', 20, 1),
('B', '6', 30, 10)
I need to get the max value by Name along with ids
I am using the following query to get the max value.
select name, ids, max(value1) as value1, max(value2) as value2
from temp
group by name,ids
The question has been tagged as Sybase ASE, but the 'create table and 'insert' commands are invalid in ASE so not sure if this is an issue of an incorrect tag or the wrong 'create table' and 'insert' commands ... so, assuming this is for a Sybase ASE database:
I'm assuming the desired output is to display those rows where value = max(value).
First we'll setup our test case:
create table mytab
(name varchar(20)
,ids varchar(20)
,value int)
go
insert into mytab (name,ids,value) values ('A', '1,2' , 10)
insert into mytab (name,ids,value) values ('A', '2,1' , 12)
insert into mytab (name,ids,value) values ('A', '1,2,3', 20)
insert into mytab (name,ids,value) values ('B', '6' , 30)
go
Here's one possible solution:
select t.name, t.ids, t.value
from mytab t
join (select name,max(value) as maxvalue from mytab group by name) dt
on t.name = dt.name
and t.value = dt.maxvalue
order by t.name
go
name ids value
-------------------- -------------------- -----------
A 1,2,3 20
B 6 30
The subquery/derived-table gives us the max(value) for each unique name. The main query then joins these name/max(value) pairs back to the main table to give us the desired rows (ie, where value = max(value)).
Tested on ASE 15.7 SP138.

Which column caused the insertion error

The error is
Conversion failed when converting the varchar value 'b' to data type int.
Table structure:
CREATE TABLE [dbo].[Table_1]
(
[columnA] varchar(50) NULL,
[ColumnB] int NOT NULL,
ColumnC int null
) ON [PRIMARY]
GO
INSERT INTO [dbo].[Table_1] ([columnA], [ColumnB], ColumnC)
VALUES (23, 56, 'b')
My ETL pipe line logs all error. Some are useful, such as
Cannot insert the value NULL into column 'ColumnB', table 'DWDev.dbo.Table_1', column does not allow nulls. INSERT fails'
whilst others are relatively useless.
How can I improve the usefulness of error message that I cannot directly action, such as that in the first sentence above?
For ColumnA DataType is Varchar(50), you need to decorate the value with single quotes And ColumnC Datatype is Int, you can't insert 'B', it should be an integer value.
The Order of Values you provided should be same as order of column specified in insert into Clause
you can correct your query in 2 ways
INSERT INTO [dbo].[Table_1]
([columnA]
,[ColumnB]
,ColumnC)
VALUES
('23',
56,
1)
OR
INSERT INTO [dbo].[Table_1]
([ColumnB]
,ColumnC,
[columnA])
VALUES
(23,
56,
'b')

Smart Many to Many Query

I have a list of item descriptions in a c# application. What I want is when I select
1 or 2 or more item descriptions of that list (checkbox list) to predict via an sql query to a many to many table what my item is (minimizing each time the possible predictions);
For example
item 1: white,green,blue
item 2: white,red,cyan
item 3: red,blue,purple
user should select from a check list
white->query will return item 1,2
white&green->query will return only item 1
From your humble description of the problem, I suppose you want something like this:
CREATE TABLE items (
item_id INT NOT NULL PRIMARY KEY IDENTITY(1,1),
name VARCHAR(100) NOT NULL
)
CREATE TABLE colors (
color_id INT NOT NULL PRIMARY KEY IDENTITY(1,1),
name VARCHAR(100) NOT NULL
)
CREATE TABLE items_colors (
item_id INT NOT NULL FOREIGN KEY REFERENCES items(item_id),
color_id INT NOT NULL FOREIGN KEY REFERENCES colors(color_id),
PRIMARY KEY(item_id, color_id),
)
INSERT INTO items(name) VALUES ('item 1')
INSERT INTO items(name) VALUES ('item 2')
INSERT INTO items(name) VALUES ('item 3')
INSERT INTO colors(name) VALUES ('white')
INSERT INTO colors(name) VALUES ('green')
INSERT INTO colors(name) VALUES ('blue')
INSERT INTO colors(name) VALUES ('red')
INSERT INTO colors(name) VALUES ('cyan')
INSERT INTO colors(name) VALUES ('purple')
INSERT INTO items_colors(item_id, color_id) VALUES (1, 1)
INSERT INTO items_colors(item_id, color_id) VALUES (1, 2)
INSERT INTO items_colors(item_id, color_id) VALUES (1, 3)
INSERT INTO items_colors(item_id, color_id) VALUES (2, 1)
INSERT INTO items_colors(item_id, color_id) VALUES (2, 4)
INSERT INTO items_colors(item_id, color_id) VALUES (2, 5)
INSERT INTO items_colors(item_id, color_id) VALUES (3, 3)
INSERT INTO items_colors(item_id, color_id) VALUES (3, 4)
INSERT INTO items_colors(item_id, color_id) VALUES (3, 6)
SELECT i.*
FROM items i
WHERE 2 = (
SELECT COUNT(*)
FROM items_colors ic
JOIN colors c
ON ic.color_id = c.color_id
WHERE i.item_id = ic.item_id
AND c.name IN ('white', 'green')
)
Within "IN" clause you should provide list of values that user has selected in the UI (you have to build list of parameters dynamically). You also have to provide number of elements that user has selected ("2" in my example solution).
So the query in application will look like this:
SELECT i.*
FROM items i
WHERE #count = (
SELECT COUNT(*)
FROM items_colors ic
JOIN colors c
ON ic.color_id = c.color_id
WHERE i.item_id = ic.item_id
AND c.name IN (#color1, #color2, ..., #colorN)
)
(Where #count is the number of #colorX parameters.)

Resources