SQL Server - IN clause with multiple fields - sql-server

Is it possible to include in a IN clause multiple fields? Something like the following:
select * from user
where code, userType in ( select code, userType from userType )
I'm using ms sql server 2008
I know this can be achieved with joins and exists, I just wanted to know if it could just be done with the IN clause.

Not the way you have posted. You can only return a single field or type for IN to work.
From MSDN (IN):
test_expression [ NOT ] IN
( subquery | expression [ ,...n ]
)
subquery - Is a subquery that has a result set of one column.
This column must have the same data type as test_expression.
expression[ ,... n ] - Is a list of expressions to test for a match.
All expressions must be of the same type as
test_expression.
Instead of IN, you could use a JOIN using the two fields:
SELECT U.*
FROM user U
INNER JOIN userType UT
ON U.code = UT.code
AND U.userType = UT.userType

You could use a form like this:
select * from user u
where exists (select 1 from userType ut
where u.code = ut.code
and u.userType = ut.userType)

Only with something horrific, like
select * from user
where (code + userType) in ( select code + userType from userType )
Then you have to manage nulls and concatenating numbers rather than adding them, and casting, and a code of 12 and a usertype of 3 vs a code of 1 and a usertype of 23, and...
..which means you start heading into perhaps something like:
--if your SQLS supports CONCAT
select * from user
where CONCAT(code, CHAR(9), userType) in ( select CONCAT(code, CHAR(9), userType) from ... )
--if no concat
select * from user
where COALESCE(code, 'no code') + CHAR(9) + userType in (
select COALESCE(code, 'no code') + CHAR(9) + userType from ...
)
CONCAT will do a string concatenation of most things, and won't zip the whole output to NULL if one element is NULL. If you don't have CONCAT then you'll string concat using + but anything that might be null will need a COALESCE/ISNULL around it.. And in either case you'll need something like CHAR(9) (a tab) between the fields to prevent them mixing.. The thing between the fields should be southing that is not naturally present in the data..
Tis a shame SQLS doesn't support this, that Oracle does:
where (code, userType) in ( select code, userType from userType )
but it's probably not worth switching DB for; I'd use EXISTS or a JOIN to achieve a multi column filter
So there ya go: a solution that doesn't use joins or exists.. and a bunch of reasons why you shouldn't use it ;)

How about this instead:
SELECT user.* FROM user JOIN userType on user.code = userType.code AND user.userType = userType.userType

You can either use joins
SELECT * FROM user U
INNER JOIN userType UT on U.code = UT.code
AND U.userType = UT.userType

I had to do something very similar but EXISTS didn't work in my situation. Here is what worked for me:
UPDATE tempFinalTbl
SET BillStatus = 'Non-Compliant'
WHERE ENTCustomerNo IN ( SELECT DISTINCT CustNmbr
FROM tempDetailTbl dtl
WHERE dtl.[Billing Status] = 'NEEDS FURTHER REVIEW'
AND dtl.CustNmbr = ENTCustomerNo
AND dtl.[Service] = [Service])
AND [Service] IN ( SELECT DISTINCT [Service]
FROM tempDetailTbl dtl
WHERE dtl.[Billing Status] = 'NEEDS FURTHER REVIEW'
AND dtl.CustNmbr = ENTCustomerNo
AND dtl.[Service] = [Service])
EDIT: Now that I look, this is very close to #v1v3kn's answer

I don't think that query is quite portable,it would be safer to use something like
select * from user
where code in ( select code from userType ) and userType in (select userType from userType)

select * from user
where (code, userType) in ( select code, userType from userType );

Related

Results returned from view in SQL Server differ depending on whether "=" or "IN" used

I have a view created in SQL Server 2008 that appears to be giving different results depending on whether I user "=" or "IN" when querying it.
For example, I can run:
SELECT * FROM UsersToMigrate WHERE u_s_p_no = 123
...and it returns no records.
However, if I run:
SELECT * FROM UsersToMigrate WHERE u_s_p_no in (123, 456)
...then it returns records for both values, as expected.
The view is based on 4 subqueries that are UNION ALL'd together. The first subquery uses the analytical function row_number():
Select
Replace(SPMF.FirstName,',',' ') [FirstName],
Replace(SPMF.Surname,',',' ') [SurName],
SPMF.sp_email [Email],
UA.username,
UA.DomainLogon [DomainLogon],
Case When SPMF.TeamLevel = 3 And CL.cw_type in (2,6,10,14) Then 'Adult Social Care - Practitioner'
When SPMF.TeamLevel = 2 And CL.cw_type in (2,6,10,14) Then 'Adult Social Care - Team Manager'
When SPMF.TeamLevel = 3 And CL.cw_type in (1,4,5,9,12,13) Then 'Children Social Care - Practitioner'
When SPMF.TeamLevel = 2 And CL.cw_type in (1,4,5,9,12,13) Then 'Children Social Care - Team Manager'
When CL.cw_type in (2,6,10,14) Then 'Adult Social Care - Practitioner'
When CL.cw_type in (1,4,5,9,12,13) Then 'Children Social Care - Practitioner'
Else 'Adult Social Care - Practitioner'
End [Role],
password [EncryptedPassword],
CL.OwningBusinessUnit [BusinessUnit],
Case When ISNULL(UA.Status,1) = 1 Then 0 Else 1 End [Disabled],
null [Owner],
UA.date_setup [CreatedOn],
u_s_p_no,
defaultRole,
CL.cw_type [cw_type]
From (select * from (
select *, row_number() over (partition by u_s_p_no order by status) as rownum1
from useraccess) as x
where rownum1 = 1) UA
Inner Join (select * from (
select *, row_number() over (partition by s_p_no order by s_p_no) as rownum2
from ServiceProviderMasterFile) as y
where rownum2 = 1) SPMF
Left Join sp_class CL WITH (NOLOCK) On CL.Category = spmf.sp_cat and CL.class = spmf.sp_class
Where IsNull(UA.IncludeInExport,1) = 1
I'm just wondering if this analytical function might be something to do with the problem?
Has anyone experienced anything like this? Any ideas why it might be happening?
Thanks
what if you do
SELECT * FROM UsersToMigrate WHERE u_s_p_no in (123, 456);
SELECT * FROM UsersToMigrate WHERE u_s_p_no = 123;
SELECT * FROM UsersToMigrate WHERE u_s_p_no = 456;
SELECT * FROM UsersToMigrate WHERE u_s_p_no = 123 OR u_s_p_no = 456;
(1) is logically the same as (4) - the records returned by (2) and (3) should match with (4) - otherwise is data being changed somewhere?
Is the problem you described initially really the problem? Does it exist in that simple form?
What was the larger piece of SQL shown for, if the problem occurs in simpler SQL?
Why does the larger piece of SQL appear to have some stuff missing from it? Is it the 'real' SQL?

Create View - Declare a variable

I am creating a view that is using that STUFF function. I want to put the result of STUFF in a variable for my view. The problem I am having is declaring my variable. It gives me the message "Incorrect Syntax near 'DECLARE'. Expecting '(' or SELECT." I already have the '(' in there. I have tried putting a BEGIN before it. I have tried putting it after the SELECT word. But nothing seems to work and I cannot find a solution in my search. I am using SQL Server 2012
CREATE VIEW [AQB_OB].[GISREQUESTEDBURNS]
AS
(DECLARE #CONDITIONS AS varchar(20)
SET #CONDITIONS = (SELECT DISTINCT BD.[RequestedBurnsID]
,[ConditionsReasonsID] = STUFF((SELECT ', ' + CONVERT(VARCHAR (20),[ConditionsReasonsID]) FROM [AQB_OB].[BurnDecisions] WHERE [RequestedBurnsID]= BD.[RequestedBurnsID] ORDER BY [RequestedBurnsID] ASC
FOR XML PATH ('')) , 1 , 1, '') FROM
[AQB_OB].[BurnDecisions] BD)
SELECT RB.[RequestedBurnsID] AS REQUESTEDBURNID
,BUY.[BurnYear] AS BURNYEAR
,CY.[CurrentYear] AS CURRENTYEAR
,RB.[BurnSitesID] AS BURNSITESID
,[BurnerID] AS BURNERID
,[Contact] AS CONTACT
,[BurnDecision] AS BURNDECISION
,RB.[Comment] AS COMMENT
,#CONDITIONS AS CONDITIONS
FROM [AQB_MON].[AQB_OB].[RequestedBurns] RB
LEFT join AQB_MON.[AQB_OB].[PileDryness] PD on RB.[PileDrynessID] = PD.[PileDrynessID]
inner join AQB_MON.[AQB_OB].[BurnYear] BUY on BUY.BurnYearID = BP.BurnYearID
inner join AQB_MON.[AQB_OB].[CurrentYear] CY on CY.CurrentYearID = BUY.CurrentYearID
GO
You can't declare variables in a view. Could you make it into a function or stored procedure?
Edit - you might also be able to put something into a CTE (Common Table Expression) and keep it as a view.
e.g.
WITH conditions as
(
... do the STUFF here
)
SELECT blah
FROM blah
INNER JOIN conditions
(or CROSS JOIN conditions if its just one row, I can't quite decipher what your data is like)
Here is a sample query that uses a CTE (Common Table Expression) to nicely emulate internal variable construction, as described by James Casey. You can test-run it in your version of SQL Server.
CREATE VIEW vwImportant_Users AS
WITH params AS (
SELECT
varType='%Admin%',
varMinStatus=1)
SELECT status, name
FROM sys.sysusers, params
WHERE status > varMinStatus OR name LIKE varType
SELECT * FROM vwImportant_Users
yielding output:
status name
12 dbo
0 db_accessadmin
0 db_securityadmin
0 db_ddladmin
also via JOIN
WITH params AS ( SELECT varType='%Admin%', varMinStatus=1)
SELECT status, name
FROM sys.sysusers INNER JOIN params ON 1=1
WHERE status > varMinStatus OR name LIKE varType
also via CROSS APPLY
WITH params AS ( SELECT varType='%Admin%', varMinStatus=1)
SELECT status, name
FROM sys.sysusers CROSS APPLY params
WHERE status > varMinStatus OR name LIKE varType
Or use a CTE (common table expression) as subselect like:
WITH CTE_Time(Clock)
AS(
SELECT 11 AS [Clock] -- set var
)
SELECT
DATEPART(HOUR, GETDATE()) AS 'actual hour',
CASE
WHEN DATEPART(HOUR, GETDATE()) >= (SELECT [Clock] FROM CTE_Time) THEN 'after'
ELSE 'before'
END AS [Data]
Try put the condition subquery directly inside the the view select statement. you may CAST the XML to VARCHAR(20).
CREATE VIEW [AQB_OB].[GISREQUESTEDBURNS]
AS
SELECT RB.[RequestedBurnsID] AS REQUESTEDBURNID
,BUY.[BurnYear] AS BURNYEAR
,CY.[CurrentYear] AS CURRENTYEAR
,RB.[BurnSitesID] AS BURNSITESID
,[BurnerID] AS BURNERID
,[Contact] AS CONTACT
,[BurnDecision] AS BURNDECISION
,RB.[Comment] AS COMMENT,
(
SELECT DISTINCT BD.[RequestedBurnsID],
[ConditionsReasonsID] = STUFF((SELECT ', ' + CONVERT(VARCHAR (20), [ConditionsReasonsID]) FROM [AQB_OB].[BurnDecisions]
WHERE [RequestedBurnsID]= BD.[RequestedBurnsID] ORDER BY [RequestedBurnsID] ASC
FOR XML PATH ('')) , 1 , 1, '') FROM
[AQB_OB].[BurnDecisions] BD
) AS CONDITIONS
FROM [AQB_MON].[AQB_OB].[RequestedBurns] RB
LEFT join AQB_MON.[AQB_OB].[PileDryness] PD on RB.[PileDrynessID] = PD.[PileDrynessID]
inner join AQB_MON.[AQB_OB].[BurnYear] BUY on BUY.BurnYearID = BP.BurnYearID
inner join AQB_MON.[AQB_OB].[CurrentYear] CY on CY.CurrentYearID = BUY.CurrentYearID

How can I prevent getting duplicates with this UNION select (Or a different method)

I am trying to get a certain set of results with a UNION SELECT, and I am having trouble figuring out how to limit my results accordingly. Essentially I have objects that I am returning and displaying in the form of several of their attributes. One of those attributes is a comment that can be made on the object. Some have comments on them, and some do not. I want to display all that have comments as well as all that do not have comments. The problem I am running into: My first SELECT is returning all of the instances with comments just fine, but my second SELECT which is meant to return the instances without comments, not only does that, but also returns a duplicate instance of the previous, but without a comment. So in the end I have all instances where there is no comment, and if there is a comment, I get in instance with the comment and a duplicate but without the comment. I have pasted my code below, but if you feel there is a better way of doing this other than using a union select, your enlightenment is much appreciated.
Some Background: In my DB, I have my main table of 'Deals' and a Deal can have many comments, and I am using the DealID with a marker to tell if that Comment is active or not.
My Stored Procedure:
#Dealership nvarchar(50)
AS
SELECT DealDate,
DateReceived,
Bounced,
StockNumber,
LocationName,
CustomerName,
Comment
FROM
tVehicleDeal,
tDealerships,
tInternalLocations,
tVehicleComments,
tCustomer
WHERE
(tVehicleDeal.DealershipID = tDealerships.DealershipID)
AND (tDealerships.DealershipName LIKE '%'+#Dealership+'%')
AND tVehicleDeal.InternalLocationID = tInternalLocations.InternalLocationID
AND tVehicleDeal.DealID = tVehicleComments.DealID
AND tVehicleDeal.CustomerID = tCustomer.CustomerID
AND NOT tInternalLocations.LocationName = 'Down Deal'
AND tVehicleDeal.Titled IS NULL
AND tVehicleDeal.Archived = 0
UNION SELECT DealDate,
DateReceived,
Bounced,
StockNumber,
LocationName,
CustomerName,
NULL
FROM
tVehicleDeal,
tDealerships,
tInternalLocations,
tCustomer
WHERE
(tVehicleDeal.DealershipID = tDealerships.DealershipID)
AND (tDealerships.DealershipName LIKE '%'+#Dealership+'%')
AND tVehicleDeal.InternalLocationID = tInternalLocations.InternalLocationID
AND tVehicleDeal.CustomerID = tCustomer.CustomerID
AND NOT tInternalLocations.LocationName = 'Down Deal'
AND tVehicleDeal.Titled IS NULL
AND tVehicleDeal.Archived = 0
ORDER BY [DealDate] ASC
-Thanks in advance!
You need to read up on the SQL-92 Join syntax. You need to use an Outer Join to the comments table.
try this:
SELECT DealDate, DateReceived, Bounced, StockNumber,
LocationName, CustomerName, Comment
FROM tVehicleDeal vd
Join tDealerships d
On d.DealershipID = vd.DealershipID
And d.DealershipName LIKE '%'+#Dealership+'%'
Join tInternalLocations il
On il.InternalLocationID = vd.InternalLocationID
And il.LocationName <> 'Down Deal'
Join tCustomer cu
On cu.CustomerID = vd.CustomerID
Left Join tVehicleComments c
On c.DealID = vd.DealId
WHERE vd.Titled Is Null
And vd.Archived = 0
If the results on both sides aren't EXACTLY the same you'll get both. Since you have the NULL in place of Comment in the second query, the result for that row is different from the result in the first query and thus you get both results.
One way to fix it would be to add a
AND Comment IS NULL
to the second query.
Can I suggest that you
Use JOIN syntax instead of the WHERE
Use a LEFT OUTER JOIN to tVehicleComments
and this way you should be able to avoid the repeated query with the UNION entirely, e.g.
Edit
If you potentially have more than one comment per vehicledeal, you can restrict the output to just one of them by grouping by the remainder of columns and applying an appropriate Aggregation function to the comment, e.g.:
SELECT
DealDate,
DateReceived,
Bounced,
StockNumber,
LocationName,
CustomerName,
MIN(Comment) AS FirstComment
FROM
tVehicleDeal
INNER JOIN tDealerships
ON tVehicleDeal.DealershipID = tDealerships.DealershipID
INNER JOIN tInternalLocations,
ON tVehicleDeal.InternalLocationID = tInternalLocations.InternalLocationID,
LEFT OUTER JOIN tVehicleComments
ON tVehicleDeal.DealID = tVehicleComments.DealID
INNER JOIN tCustomer
ON tVehicleDeal.CustomerID = tCustomer.CustomerID
WHERE
tDealerships.DealershipName LIKE '%'+#Dealership+'%'
AND NOT tInternalLocations.LocationName = 'Down Deal'
AND tVehicleDeal.Titled IS NULL
AND tVehicleDeal.Archived = 0
GROUP BY
DealDate,
DateReceived,
Bounced,
StockNumber,
LocationName,
CustomerName;

Using COUNT() in WHERE Clause

I have the following SQL statement:
SELECT C.LINKED_TABLE_ID AS CLIENT_DIWOR, I.STATUS AS AML_STATUS, dbo.CLIENT_MASTER.CLIENTCODE
FROM AML_INFORMATION AS I INNER JOIN
dbo.COMM_ENTRY AS C ON C.NAME_ID = I.CONTACT_ID AND C.TABLE_ID = 'C' AND C.PRIMARY_FLAG = 'Y' INNER JOIN
dbo.CLIENT_MASTER ON C.LINKED_TABLE_ID = dbo.CLIENT_MASTER.DIWOR
WHERE I.CONTACT_ID = 234
AND I.[STATUS] = 'CC'
AND (CLIENT_MASTER.DIWOR = I.CONTACT_ID)
AND (CLIENT_MASTER.POSTING_STATUS <> '')
AND ((SELECT COUNT(CONTACT_ID) FROM AML_ID_DOCUMENT GROUP BY CONTACT_ID HAVING CONTACT_ID = 234) >1)
If I run this it returns 0 records, however if I remove the last AND statement AND ((SELECT COUNT(CONTACT_ID) FROM AML_ID_DOCUMENT GROUP BY CONTACT_ID HAVING CONTACT_ID = 234) >1) it returns the records I would expect.
Is it possible to use a COUNT() in this way? Incidentally, the COUNT() in this example returns 2 records and if I include it in the SELECT statement I also get 0 records returned.
Can anyone point me in the right direction?
Thanks in advance.
Your showed Script should never return anything ...
your having part could look this way
and (Select Count(*) from AML_ID_DOCUMENT where CONTACT_ID =I.CONTACT_ID)>1
though ist should work as it is...
Shouldn't the last part be like this:
AND EXISTS (SELECT CONTACT_ID FROM AML_ID_DOCUMENT
WHERE CONTACT_ID = 234
GROUP BY CONTACT_ID
HAVING COUNT(CONTACT_ID)>1)
Try this query:
select count(portfolio_id) as porfolioCount
from engineering_module_tracker_v2
where r0_approval__category='Cat I' & 'Cat II';
Cat means category-wise.

How can I create multiple columns from one DB Field in SQL Server?

I have a field called PropertyValue in the UserProfile table that can contain information for address, phone number, first name, last name, user name, city, etc... each record in this table is associated to a user by UserId, it is also associated to a ProfilePropertyDefinition which contains the definition for each of the properties (ie. PropertyName).
Through this relationship I can get all of the property values along with their property names. What I would like to do it to extract the data from these two columns (PropertyValue, PropertyName) and create a table similar to this:
First Name | Last Name | Email | Phone | City | Country
-------------------------------------------------------
| | | | |
So, I wanted to know if I can use a SQL statement to do this, here's my go at it:
SELECT FirstName = (SELECT PropertyValue FROM UserProfile WHERE PropertyDefinitionID = (SELECT PropertyDefinitionID WHERE PropertyName = 'first name')),
LastName = (SELECT PropertyValue FROM UserProfile WHERE PropertyDefinitionID = (SELECT PropertyDefinitionID WHERE PropertyName = 'last name')),
Email = (SELECT PropertyValue FROM UserProfile WHERE PropertyDefinitionID = (SELECT PropertyDefinitionID WHERE PropertyName = 'email'))
But that didn't work and something seems really weird about it... Anyone know how to take one column and display it's values in several different columns?
SELECT fn.PropertyValue FirstName,
ln.PropertyValue LastName,
etc...
From UserProfile fN
Join UserProfile lN
On fN.PropertyName = 'first name'
And ln.PropertyName = 'last name'
And fn.user = ln.user
Join UserProfile eM
On fN.PropertyName = 'first name'
And eM.PropertyName = 'email'
And eM.user = fn.user
(
Personally, I would stop right now and consider how bad this design will be for performance. This is in general a very poor technique to use to store this type of data. If you have 20 proerties you want to display you will have to join (And left join at that as you can't guarantee each property will be represented) to this table 20 times. Further, if this is central to your data structure (As it sounds like it is from the type of data you seem to be storing) virtually every query will need to do something simliar and performance will be atrocious. There are time when this is the best model (when you have no way of knowing in advance what properties will need to be stored), but most of the time, it's use is a sign of a bad design.
http://en.wikipedia.org/wiki/Entity-Attribute-Value_model
I guess you could do a select from the same table multiple times.
Let say tA is the name table with UserProfileID,PropertyDefinition and PropertyValue
You could do
select
t1.PropertyValue as FirstName,
t2.PropertyValue as LastName,
...
FROM
tA as t1, tA as t2, ....
WHERE
t1.PropertyDefinition Like 'FirstName' AND
t2.PropertyDefinition Like 'LastName' AND
....
AND
t1.UserId = #user AND
t2.UserID = #user ....
Not ideal, but it would work
You need to join the tables many times over (as many times as you have fields):
SELECT UPFN.PropertyValue AS FirstName, UPLN.PropertyValue AS LastName, ...
FROM UserProfile UPFN
INNER JOIN ProfilePropertyDefinition PPDFN ON PPDFN.PropertyDefinitionID = UPFN.PropertyDefinitionID AND PPDFN.PropertyName = 'first name'
INNER JOIN UserProfile UPLN ON UPLN.id = UPFN.id
INNER JOIN ProfilePropertyDefinition PPDLN ON PPDLN.PropertyDefinitionID = UPLN.PropertyDefinitionID AND PPDLN.PropertyName = 'last name'
...
Note that this relies on their being some ID field in the UserProfile that you can use to tie all the rows for the same user together.
Assuming schema like
UserProfile#
{userid,
ProfileName,
propertyValue
}
You would want to do
SELECT
FirstName.PropertyValue FirstNAme,
LastName.PropertyValue LastName,
FROM
users
JOIN (USERPROFILE) FirstName ON
FirstName.userid = users.userid
and PropertName ='FirstName'
JOIN (USERPROFILE) LastName ON
LastName.userid = users.userid
and PropertName ='LastName'
I would write the query like this:
Select
aa.userId,
Coalesce(Max(Case when PropertyName = 'First Name' then PropertyValue else '' end),'') as FirstName,
and so on
from
UserTable as aa
left join
UserProfile as bb
on
aa.UserId = bb.UserId
left join
ProfilePropertyDefinition as cc
on bb.PropertyDefinitionId = cc.PropertdefinitionId
group by
aa.UserId
I would need to know more about your table sturtures and what you are trying to achomplish but an option may be to create a SQL Scalar Function to retieve the values of the properties. I am making some assumptions on table names and database setup but try this on...
CREATE FUNCTION [dbo].[UserProperty]
(
#UserProfileID UNIQUEIDENTIFIER, #Property VARCHAR(200)
)
RETURNS VARCHAR(max)
AS
BEGIN
-- Declare the return variable here
DECLARE #Value AS VARCHAR(MAX)
SELECT #Value = PropertyValue FROM UserProfile up INNER JOIN PropertyDefinitions pd ON
up.PropertyDefinitionID = pd.PropertyDefinitionID
WHERE pd.PropertyName = #Property AND up.UserProfileID=#UserProfileID
RETURN ISNULL(#Value,'')
END
SELECT
[dbo].[UserProperty](UserProfileID, 'first name') AS [First Name],
[dbo].[UserProperty](UserProfileID, 'last name') AS [Last Name],
[dbo].[UserProperty](UserProfileID, 'email') AS [Email]
FROM
[Users]

Resources