set a range for an IN statement - sql-server-2012

Is it possible to set a range of products for use in a Select IN statement
for example can I rewrite the following:
Select Country, SUM(POPULATION) as cPopulation
FROM MyTable
WHERE Country IN ('FRANCE', 'GERMANY', 'SPAIN')
To
DECLARE #Countries nvarchar(max)
SET #Countries = 'FRANCE', 'GERMANY', 'SPAIN' /*is this possible?*/
Select Country, SUM(POPULATION) as cPopulation
FROM MyTable
WHERE Country IN (#Countries)
Is this even possible
Kind Regards

No, you have to create a table variable or temp table and do the IN or join or something like that.
You can do
Where '/France/Germany/spain/' like '%/'+country+'/%'
But performance will suffer as it will do a scan.

Related

SQL if else filter in single select statement

Is it possible to output the following:
for each group by personId, if email ends with '#company.com' then filter by [where type = 'h'] else filter [where type = 't']
so the query would output the following:
1 bob#hotmail.com h
2 bill#hotmail.com t
create table #emails (personId int, email nvarchar(100), type char(1) )
insert into #emails values (1, 'bob#company.com', 't');
insert into #emails values (1, 'bob#hotmail.com', 'h');
insert into #emails values (2, 'bill#hotmail.com', 't');
insert into #emails values (2, 'bill#gmail.com', 'h');
select * from #emails
drop table #emails
Thanks
This is based on the description of the logic, not the sample results.
I think you want boolean logic in the where clause:
where (email like '%#company.com' and type = 'h') or
(email not like '%#company.com' and type = 't')
This particular version assumes that email is never NULL. It is easy enough to incorporate that into the logic.
EDIT:
I see, this is a prioritization query:
select top (1) with ties e.*
from #emails e
order by row_number() over (partition by personId
order by (case when email like '%#company.com' and type = 'h' then 1
when type = 't' then 2
else 3
end)
);
You can exclude type column from the insert and use a computed field instead, as below:
create table #emails (personId int, email nvarchar(100), type as case when email like '%#company.com' then 'h' else 't' end)
Like that all your inserts will automatically have the type column handled
EDIT: If you still want to perform an update after words just use the same CASE statement in the select.
It's kinda hard to understand the question, but I think you are looking for something like this:
SELECT personId, email, type
FROM #emails t0
WHERE type = CASE WHEN EXISTS(
SELECT 1
FROM #emails t1
WHERE t0.personId = t1.personId
AND t1.email LIKE '%#company.com'
) THEN 'h' ELSE 't' END
This will give the desired results, so the text of the question should be something like "when there is a record for that person with email ends with #company.com then type h otherwise type t.
If I get it right. Your #company.com is a default value to email column. So, you want to get the actual email, which will be the next email for each person. I'm not sure about the type column why you're trying to using it, as (in my opinion) doesn't matter if is it h or t as long as we can exclude emails that ends with #company.com directly with this simple query :
SELECT *
FROM #emails
WHERE
RIGHT(email, 11) <> 'company.com'
from there you can expand the where clause as much conditions as needed.

Do I need a where clause in a conditional UPDATE?

We imported a lot of data from another table. Now I'm trying to correct some of them.
UPDATE [x10ddata].[dbo].[ResourceTest]
SET [Country] = (CASE
WHEN [Country] IN ('Aezerbaijan', 'AZERBIJAN') THEN 'Azerbaijan'
WHEN [Country] = 'Belgique' THEN 'Belgium'
WHEN [Country] = 'China (RPC)' THEN 'China'
WHEN [Country] = 'Columbia' THEN 'Colombia'
WHEN [Country] = 'Croatia (Local Name: Hrvatska)' THEN 'Croatia'
.....//...
WHEN [Country] IN ('U.S.', 'U.S.A', 'U.S.A.', 'US', 'USA',
'USA - Maryland', 'USAQ') THEN 'United States'
END)
GO
I didn't use ELSE because many rows have valid country. My question is to know whether I need to the WHERE clause to filter the rows that will be affected?
The reason I'm asking this question is that, I've selected into a test table and tried the the script. According to the output, all the rows affected, but when I check closely, not all the rows were affected. It's confusing.
Thanks for helping
The case statement will return null if none of the when clauses are met. You can verify this with this simple sql:
declare #i int
set #i = 2
select case when #i = 1 then 'A' end AS Column1
This will return null since #i is not 1.
To fix this in your case, you can either add the where clause like you said, or the simpler option might be to add ELSE [Country] after all of your WHEN clauses. This would mean "If I don't need to change the country field, then just use the same value that was there before."
You won't need a WHERE clause, but the ELSE clause is needed. Change your statement to:
UPDATE [x10ddata].[dbo].[ResourceTest]
SET [Country] = (CASE
WHEN [Country] IN ('Aezerbaijan', 'AZERBIJAN') THEN 'Azerbaijan'
WHEN [Country] = 'Belgique' THEN 'Belgium'
WHEN [Country] = 'China (RPC)' THEN 'China'
WHEN [Country] = 'Columbia' THEN 'Colombia'
WHEN [Country] = 'Croatia (Local Name: Hrvatska)' THEN 'Croatia'
.....//...
WHEN [Country] IN ('U.S.', 'U.S.A', 'U.S.A.', 'US', 'USA',
'USA - Maryland', 'USAQ') THEN 'United States'
ELSE [Country]
END)
alternatively,
Make a conversion table,
DECLARE #conversion TABLE
(
[Before] NVARCHAR(250) NOT NULL,
[After] NVARCHAR(250) NOT NULL
);
INSERT #conversion
VALUES
('Aezerbaijan', 'Azerbaijan'),
...
('USAQ', 'United States');
Then do,
UPDATE [x10ddata].[dbo].[ResourceTest]
SET [Country] = [C].[After]
FROM
[x10ddata].[dbo].[ResourceTest]
JOIN
#conversion [C]
ON [C].[Before] = [C].[Country];
This has a number of potential performance benefits over the extend CASE approach, among which is only effecting rows that need to change.
Its probably worth using a temporary table instead of a table variable and creating an index on [Before] to optimize the join.
No you don't need a where clause because your CASE statements contain your logic.
Note: If a value doesn't match any of your CASE statements then it will return null. So I recommend adding ELSE [Country] at the end. Here's an example that demonstrates what I'm saying
SELECT * INTO #yourTable
FROM
(
SELECT 1 ID, CAST('OldValue' AS VARCHAR(25)) val
UNION ALL
SELECT 2 , 'OldValue'
UNION ALL
SELECT 3,'Doesnt need to be updated'
) A
SELECT *
FROM #yourTable;
Results:
ID val
----------- -------------------------
1 OldValue
2 OldValue
3 Doesnt need to be updated
Now update:
UPDATE #yourTable
SET val =
CASE
WHEN ID = 1 THEN 'NewValue1'
WHEN ID = 2 THEN 'NewValue2'
--Add this so you leave values alone if they don't match your case statements
ELSE val
END
FROM #yourTable
SELECT *
FROM #yourTable
Results:
ID val
----------- -------------------------
1 NewValue1
2 NewValue2
3 Doesnt need to be updated
No, you don't NEED it. Aside from the performance cost that may be incurred through additional (unnecessary) writes to disk and locking (blocking other sessions), the physical outcome would be the same.
One could argue that you SHOULD use a WHERE clause, not only for performance reasons, but to better capture and convey intentions.

Is a table-valued function updatable

I'm a bit confused on the update statement but here's what I have: I have these two employees and their respective alpha numeric codes.
select * from cm.bo.hotlist('08Z')
where State = 'ca'
select * from cm.bo.hotlist('06D')
where State = 'ca'
The table has certain cities associated with each employee, the top select statement has these list of cities associated with '08Z'... let's say.
New York
Chicago
I would like to move those cities to the employee '06D'
How would I got about updating?
The confusing part for me is the table is a table-valued function.
Any help would be greatly appreciated. Thank you.
maybe something like so:
update CITY cm.bo.hotlist('06D')
where CITY in (New York, Chicago)
So what you want is:
Update cm.bo.hotlist('08Z')
set
<EmployeeID Column> = '06D'
where
city in ('New York', 'Chicago')
For everyone who comes here, yes, an in-line table value function is updateable as long as the underlying data set is updateable. A code sample:
IF EXISTS(select * from sys.objects where name = 'test' and schema_id = schema_id('dbo')) BEGIN DROP TABLE dbo.test; END
CREATE TABLE dbo.test(Employee varchar(10), city varchar(10));
CREATE FUNCTION [dbo].[getEmployeeCities] ( #employee varchar(10) RETURNS TABLE AS
RETURN ( SELECT * from test where employee = #employee );
insert into dbo.test select 'A', 'Chicago';
insert into dbo.test select 'B', 'New York';
select * from dbo.test;
update dbo.getEmployeeCities('A')
set Employee = 'B'
where city = 'Chicago';
select * from dbo.test;
Update Tablename
SET employes ='06D'
where CITY IN ('NEW York', 'Chicago')

SQL: Insert data from another table in a table that contains foreign keys

In a SQL DATABASE
I have a Table Users
Id Name Age AddressId
----+------+------+-----------
Where AddressId is a foreign key to a table names Addresses
The Addresses Table:
Id Country State City ZipCode
----+---------+------+------+---------
This is a ONE-TO-ONE Relationship: Each User Has 1 address and each address has one user
I have a new table named NEWUsers
Id Name
----+------
It has only Id and Name.
What i want to do is this:
Write a script to insert all the records From the NEWUSers Table into the Users Table.
I want The Age to be default 20 for all new users
And for each new user inserted I need to create a new Address record for him
the new Address record will have all it's values (country, city, state, zipcode) equal to "abcd" except the Id which will be used to set the foreign key AddressId for the new user)
How can I do that?
I tried the following:
INSERT INTO Users(Name, Age)
Values((SELECT Name FROM NewUsers),20)
But I don't know how to create a new Address record for each user inserted and specify the foreign key accordingly.
Thanks a lot for any help
One method would be with two queries, formed like the following:
INSERT INTO Addresses (Country, State, City, ZipCode)
SELECT 'abcd', 'abcd', 'abcd', 'abcd' FROM NewUsers
INSERT INTO Users (Name, Age, AddressId)
SELECT Name, 20, ?? FROM NewUsers
Edit: One simple way to link the users to the addresses would be to temporarily set the country to the username. Then you would have a link to determine the addressId. Once the users table is populated and linked up properly, you can set country back to the default value abcd:
insert addresses (country, state, city, zipcode)
select name, 'abcd', 'abcd', 'abcd' from newusers;
insert users (name, age, addressid)
select u.name, 20, a.id from newusers u
join addresses a on a.country = u.name;
update a
set a.country = 'abcd'
from addresses a join newusers u on a.country = u.name;
Demo: http://www.sqlfiddle.com/#!3/1f09b/8
There are more complex ways to do this if you want to guarantee transactional consistency if multiple inserts can happen simultaneously, or if you want to allow duplicate names, etc. But based on the example you've given and details so far, this method should work.
This is a little hacky, but does what you want in two statements - assuming no user is going to have the name 'abcd' or enter that for their country, and that you purge the NewUsers table after this operation:
INSERT dbo.Addresses(Country, State, City, ZipCode)
OUTPUT inserted.Country, 20, inserted.id
INTO dbo.Users
SELECT Name, 'abcd', 'abcd', 'abcd'
FROM dbo.NewUsers;
UPDATE a SET Country = 'abcd'
FROM dbo.Addresses AS a
INNER JOIN dbo.NewUsers AS nu
ON a.Country = nu.Name;
You Must Write Cursor For Insert In Users and Address Table With Forign key
DECLARE #AddressID INT,
#ID INT,
#Name NVARCHAR(50)
DECLARE UserCursor CURSOR FOR
SELECT ID, NAME
FROM NewUsers
OPEN UserCursor
FETCH NEXT FROM UserCursor INTO #ID, #Name
WHILE ##FETCH_STATUS =0 BEGIN
INSERT INTO Addresses(Country, State, City, ZipCode)
VALUES ('abcd', 'abcd', 'abcd', 'abcd')
SET #AddressID = SCOPE_IDENTITY()
INSERT INTO Users(Name, Age, AddressID)
VALUES (#Name, 20, #AddressID)
FETCH NEXT FROM UserCursor INTO #ID, #Name
END
CLOSE UserCursor
DEALLOCATE UserCursor
Assuming you can set the AddressId however you want...
INSERT Addresses
(
Id,
Country,
State,
City,
ZipCode
)
SELECT
Id,
'abcd',
'abcd',
'abcd',
'abcd'
FROM NEWUsers
INSERT Users
(
Id,
Name,
Age,
AddressId
)
SELECT
Id,
Name,
20,
Id
FROM NEWUsers
If AddressId is an identity column, you can disable the identity temporarily first. See SET IDENTITY_INSERT for info and examples.
Honestly, the answer is that you don't want to do what you think you want to do. If the users have one and only one address, as you stated, then you should have only one table (users) that contains the address fields. Modeling your data properly will make this problem go away intrinsically.

How to write the sql to findout which value is not in the table?

I am having a table Student and i have a set of 20 names.
by using his sql
select name from student st where st.name in (
'abc', 'xyz', . . .
)
i can find out all student names which are in table and in the set.
Now, how can i find out which out of these 20 names are not in Student table.
I'm assuming you want the names themselves.
One option is to create a table with all the available student names, then select from it rows which don't have corresponding rows in the student tables, it will look something like this
select name from student_names
where name not in (select name from students)
CREATE TABLE student(name VARCHAR(255));
INSERT INTO student VALUES('a'), ('abc');
CREATE TABLE temp(x VARCHAR(255));
INSERT INTO temp VALUES('abc'), ('xyz');
SELECT x FROM temp WHERE
NOT EXISTS (SELECT * FROM student st WHERE st.name = x);
Depending on the database you use, there might be an easier way. There is also a way using UNION.
SELECT NOT IN ?
postgresql: http://archives.postgresql.org/pgsql-sql/2002-08/msg00322.php
DECLARE #names table ( name varchar(100) )
INSERT INTO #names VALUES ('abc')
...
INSERT INTO #names VALUES ('xyz')
SELECT name FROM #names WHERE name NOT IN ( SELECT DISTINCT Name FROM Student )
select name from student where name not in (
select name from student st where st.name in (
'abc', 'xyz', . . .
))
EDIT: I might not get what you are looking for. Please run following script and it is giving the results.
declare #student table
(
name varchar(50)
)
insert into #student select 'james'
insert into #student select 'will'
insert into #student select 'bill'
insert into #student select 'adam'
insert into #student select 'jon'
insert into #student select 'white'
insert into #student select 'green'
select name from #student where name in ('james', 'will', 'bill')
select name from #student where name not in (select name from #student where name in ('james', 'will', 'bill'))
Assuming that the tool you are using can generate dynamic sql, try generating an inline view consisting of your set of user names - like so:
select 'abc' check_name union all
select 'xyz' check_name union all
...
(The syntax of the inline view may depend on which version of SQL you are using - some versions of SQL require a from [dummy_table] clause in select statements that are not accessing a table.)
Then construct a query using this inline view with a not exists in student clause, like this:
select check_name from (
select 'abc' check_name union all
select 'xyz' check_name union all
...
) ilv where not exists
(select null from student st where st.name = ilv.check_name)