SQL Server. T-SQL. CASE IF EXISTS query - sql

I have a classification table that looks like this:
ID CLASSIFICATION
__________________
A1 BOARD
A2 SURFBOARD
A3 SURF
Then I have a category table that looks like this
CATEGORY PARENT INDENT
____________________________________
SURF NULL 3
SURFBOARD SURF 2
BOARD SURFBOARD 1
I want to make a SQL query, that returns this:
INDENT3 INDENT2 INDENT1 ID
______________________________________
SURF NULL NULL A3
SURF SURFBOARD NULL A2
SURF SURFBOARD BOARD A1
Is it possible?. I'm not getting any ideas, seems like I need to loop through the classification table and find if there is indent1, indent2 and indent3. But not sure If I can put a script in a query, or if there is some kind of query I can do to achieve this. Something like
FOREACH CLASSIFICATION
CASE EXIST CATEGORY WITH IDENT1 INDENT 1 ELSE NULL AS IDENT1,
CASE EXIST CATEGORY WITH IDENT2 INDENT 2 ELSE NULL AS IDENT2,
CASE EXIST CATEGORY WITH IDENT1 INDENT 3 ELSE NULL AS IDENT3

If your hierarchy has a maximum of 3 levels a simpler query may be:
select cl.classification as indent3, null as indent2, null as indent1, id
from classification cl
join category ca on ca.category = cl.classification
where ca.indent = 3
union
select ca2.category, cl.classification, null as indent3, id
from classification cl
join category ca on ca.category = cl.classification
join category ca2 on ca.parent = ca2.category
where ca.indent = 2
union
select ca3.category as indent3, ca2.category as indent2, cl.classification as indent1, id
from classification cl
join category ca on ca.category = cl.classification
join category ca2 on ca.parent = ca2.category
join category ca3 on ca2.parent = ca3.category
where ca.indent = 1
If you have an indefinite number of parent/child levels you might be better to search "parent hierarchy with CTE" for a more complicated but flexible method.

Your logic is a bit hard to follow, but this should product the results that you specify:
select max(case when cl.indent = 3 then cl.category end) over () as indent3,
(case when cl.indent < 3
then max(case when cl.indent = 2 and then cl.category end) over ()
end) as indent2,
(case when cl.indent < 2
then max(case when cl.indent = 1 and then cl.category end) over ()
end) as indent1,
cl.id
from category ca join
classification cl
on ca.category = cl.category

People not so god with SQL will tell you to avoid this, SQL gurus consider to treat you as one of the pack.
It somewhat hard to understand your model but I gave it a shot.
Bit tip from the coach, if you need an id, use incremental numbers from the database, it's faster, allow god index and no risk of unexpected duplicates or truncation.
CREATE TABLE cs (id char(10), classification char(20));
CREATE TABLE ct (category char(20), parent char(20), indent int);
INSERT INTO cs VALUES
('A1','BOARD'),
('A2','SURFBOARD'),
('A3','SURF');
INSERT INTO ct VALUES
('SURF',null,3),
('SURFBOARD','SURF',2),
('BOARD','SURFBOARD',1);
select cs.id, ct1.category indent1,ct2.category indent2,ct3.category indent3 from ct ct1
left join ct ct2 on ct2.category = ct1.parent
left join ct ct3 on ct3.category = ct2.parent
left join cs on cs.classification = ct1.category
Link to the code on SQLFiddle

Related

SQL query to output all results but only one specific value from a list without duplicating

I have a table of people and a table of client types (linked by a 3rd table called client type details) which these people are linked to. Client types could be 'Friend', 'Enemy', 'Alien', or 'Monster'. Some people can have more than one of these and some people have none.
What I'm really trying to get is an output something like:
ID | Name | Friend | Enemy | Alien | Monster |
35 | John | Friend | -blank- | -blank- | Monster |
42 | Eric | -blank- | -blank- | -blank- | -blank- |
So John is both a Friend and a Monster whereas Eric isn't any. With the query I have tried creating (just with a column for the Friends in the first instant) I am getting a row for everyone but for those who are Friends I am getting 2 rows for them - one to say they are a Friend and one to say NULL
Does any of this make sense?
Query below:
SELECT DISTINCT
cl.ClientID,
cl.dcFirstName,
(SELECT Dwh.DimClientTypes.dctName
WHERE (Dwh.DimClientTypes.dctGuid IN ('52CD80A6-D4D7-4FD3-8AE8-644A40FEC108'))
) AS Friend
FROM Dwh.DimClientTypeDetails
LEFT OUTER JOIN Dwh.DimClientTypes ON Dwh.DimClientTypeDetails.dctdTypeGuid = Dwh.DimClientTypes.dctGuid
LEFT OUTER JOIN Dwh.DimClients AS cl ON Dwh.DimClientTypeDetails.dctdClientGuid = cl.dcClientGUID
I'm really not sure the best way of approaching it so any help/advice would be very gratefully received.
Thanks
Lee
You are basically looking for a pivot with strings, and you could write it using the pivot or apply but this seems simpler.
select
Id = cl.ClientId
, Name = cl.dcFirstName
, Friend = max(case when ct.dctName = 'Friend' then ct.dctName else null end)
, Enemy = max(case when ct.dctName = 'Enemy' then ct.dctName else null end)
, Alien = max(case when ct.dctName = 'Alien' then ct.dctName else null end)
, Monster = max(case when ct.dctName = 'Monster' then ct.dctName else null end)
from Dwh.DimClients as cl
left join Dwh.DimClientTypeDetails ctd on ctd.dctdClientGuid = cl.dcClientguid
left join Dwh.DimClientTypes ct on ct.dctGuid = ctd.dctdTypeGuid
group by cl.ClientId, cl.dcFirstName
For a pivot version, this is a good example: http://rextester.com/XDACE35377
create table #t (Id int not null, Name varchar(32) not null, ClientType varchar(32) null)
insert into #t values
(35, 'John', 'Friend')
,(35, 'John', 'Monster')
,(42, 'Eric', null);
select
Id
, Name
, pvt.Friend
, pvt.Enemy
, pvt.Alien
, pvt.Monster
from #t
pivot (max(ClientType) for ClientType in ([Friend],[Enemy],[Alien],[Monster])) pvt
This pivot could be done on your schema with something like this:
with c as (
select
Id = cl.ClientId
, Name = cl.dcFirstName
, ClientType = ct.dctName
from Dwh.DimClients as cl
left join Dwh.DimClientTypeDetails ctd on ctd.dctdClientGuid = cl.dcClientguid
left join Dwh.DimClientTypes ct on ct.dctGuid = ctd.dctdTypeGuid
)
select
Id
, Name
, pvt.Friend
, pvt.Enemy
, pvt.Alien
, pvt.Monster
from c
pivot (max(ClientType) for ClientType in ([Friend],[Enemy],[Alien],[Monster])) pvt

How to combine columns in sql based on another field

I have a table in SQL Server 2008 R2 like this:
Acc_id Bench-1 Bench-2
-------------------------------
1 xx
1 vv
2 pp
2 ii
3 kk
4 ll
Now, I want to combine this table on the basis of Acc_id column and get something like:
Acc_id Bench-1 Bench-2
---------------------------------
1 xx vv
2 pp ii
3 kk
4 ll
So, could someone please help me out.
SELECT ISNULL(b1.Acc_id,b2.Acc_id) as Acc_id,
b1.data,
b2.data
FROM Bench-1 AS b1 FULL OUTER JOIN
Bench-2 AS b2 ON b2.Acc_id = b1.Acc_id
Check Below query
SELECT DISTINCT a.acc_id,
b.bench_1,
c.bench_2
FROM table1 a
LEFT OUTER JOIN (SELECT acc_id,
bench_1
FROM table1
WHERE Isnull(bench_1, '') <> '') b
ON a.acc_id = b.acc_id
LEFT OUTER JOIN (SELECT acc_id,
bench_2
FROM table1
WHERE Isnull(bench_2, '') <> '') c
ON a.acc_id = c.acc_id
I would do this like:
select
Acc_id,
max([Bench-1)] as [Bench-1],
max(([Bench-2]) as [Bench-2]
from
myTable
group by
Acc_id
This assumes Acc_id won't have multiple rows with data in the same columns
If that is the case then your knowledge of the use of the results will come into play. I often will perform this more completely like
select
Acc_id,
min([Bench-1)] as [Bench-1Min],
max([Bench-1)] as [Bench-1Max],
Count([Bench-1)] as [Bench-1Count],
min([Bench-2)] as [Bench-2Min],
max([Bench-2)] as [Bench-2Max],
Count([Bench-2)] as [Bench-2Count],
from
myTable
group by
Acc_id
All this depends on the actual complexity of the real data and what you want to do with the results. If it IS actually as simple as you example the multi-join solution may work for you, but I often find that in more complex summarizations the group by solution give me results and performance I need.

Query to Not selecting columns if multiple

I've been scratching my head about this.
I have a table with multiple columns for the same project.
However, each project can have multiple rows of a different type.
I would like to find only projects type O and only if they don't have other types associated with them.
Ex:
Project_Num | Type
1 | O
1 | P
2 | O
3 | P
In the case above, only project 2 should be returned.
Is there a query or a method to filter this information? Any suggestions are welcome.
If I understand correctly, you want to check that the project has only record for its project number and it has type 'O'. You can use below query to implement this:
;with cte_proj as
(
select Project_Num from YourTable
group by Project_Num
having count(Project_Num) = 1)
select Project_Num from cte_proj c
inner join YourTable t on c.Project_Num = t.Project_Num
where t.Type = 'O'
You can do this using not exists:
select p.*
from projects p
where type = 'O' and
not exists (select 1
from projects p2
where p2.project_num = p.project_num and p2.type <> 'O'
);
You can also do this using aggregation:
select p.project_num
from projects p
group by p.project_num
having sum(case when p.type = 'O' then 1 else 0 end) > 0 and
sum(case when p.type <> 'O' then 1 else 0 end) = 0;
Another option (pretty fast)
SELECT p0.*
FROM project p0
LEFT JOIN project p1 ON (p0.Type<>p1.Type AND p0.Project_Num=p1.Project_Num)
WHERE p0.Type='O' AND p1.Type IS NULL;

In Oracle how to search with sub conditions on same column

In Oracle how do I find Cars which must have Feature1 and have at lest, one out of Feature2 or Feature3. Sample table and expected result should look like below screenshot. Thanks Kiran
This should work:
select t1.car, t1.feature
from yourtable t1
inner join
( -- inner select returns the cars with the Feature1 and Feature2 or Feature3
select car, feature
from yourtable
where feature = 'Feature1'
and exists (select car
from yourtable
where feature in ('Feature2', 'Feature3'))
) t2
on t1.car = t2.car
where t1.feature in ('Feature1', 'Feature2', 'Feature3') -- this excludes any other features
see SQL Fiddle with Demo
I like to do this with GROUP BY and HAVING:
select car
from t
group by car
having max(case when feature = 'Feature1' then 1 else 0 end) = 1 and
max(case when feature in ('Feature1', 'Feature2') then 1 else 0 end) = 1
This query returns the cars. To get the featuers as well, you have to join tis back in:
select t.*
from (select car
from t
group by car
having max(case when feature = 'Feature1' then 1 else 0 end) = 1 and
max(case when feature in ('Feature1', 'Feature2') then 1 else 0 end) = 1
) c join
t
on t.car = c.car
I like this method, because the same idea can be used for handling many different similar queries -- AND conditions, OR conditions, different subgroups, and different counts.

A Simple but complex SQL query

I have a very simple MS SQL table with the following data(with column name and datatype):
TableId PersonName Attribute AttributeValue
(int) (nvarchar 50) (nvarchar 50) (bit)
----------- ----------------------- ------------------- --------------
1 A IsHuman 1
2 A CanSpeak 1
3 A CanSee 1
4 A CanWalk 0
5 B IsHuman 1
6 B CanSpeak 1
7 B CanSee 0
8 B CanWalk 0
9 C IsHuman 0
10 C CanSpeak 1
11 C CanSee 1
12 C CanWalk 0
Now, What I need as a result is the unique PersonName that have both Attribute IsHuman and CanSpeak with AttributeValue = 1.
The expected result should be (Must not include C as this one has IsHuman = 0)
PersonName
------------
A
B
Please can any expert help me in writting a SQL Query for this.
SELECT PersonName
FROM MyTable
WHERE AttributeName = 'IsHuman'
AND AttributeValue = 1
INTERSECT
SELECT PersonName
FROM MyTable
WHERE AttributeName = 'CanSpeak'
AND AttributeValue = 1;
Obviously this approach doesn't 'scale' if the criteria can vary. It could be that the relational operator you require is division, popularly known as "the supplier who supplies all parts", specifically division with remainder.
SELECT PersonName
FROM MyTable
WHERE (AttributeName = 'IsHuman' AND AttributeValue = 1) OR
(AttributeName = 'CanSpeak' AND AttributeValue = 1)
GROUP BY PersonName
HAVING COUNT(*) > 1
or
SELECT PersonName
FROM MyTable
WHERE AttributeValue = 1 AND AttributeName IN ('IsHuman', 'CanSpeak')
GROUP BY PersonName
HAVING COUNT(*) > 1
SELECT PersonName FROM MyTable
WHERE PersonName IN
(SELECT T1.PersonName FROM MyTable T1 WHERE T1.Attribute = 'IsHuman' and T1.AttributeValue='1')
AND (Attribute = 'CanSpeak' AND AttributeValue='1')
I think two inner joins may give you alright performance depending on indexing and table sizes.
SELECT t.PersonName FROM table t
INNER JOIN table t2 ON t.PersonName=t2.PersonName AND t3.Attribute = 'IsHuman' AND t2.AttributeValue = 1
INNER JOIN table t3 ON t2.PersonName=t3.PersonName AND t3.Attribute = 'CanSpeak' AND t3.AttributeValue = 1
or
SELECT t.PersonName FROM table t
INNER JOIN table t2 ON t.PersonName=t2.PersonName
INNER JOIN table t3 ON t2.PersonName=t3.PersonName
WHERE t2.Attribute = 'IsHuman' AND t2.AttributeValue = 1 AND t3.Attribute = 'CanSpeak' AND t3.AttributeValue = 1
This solution could be simplified significantly however should the properties IsHuman and CanSpeak were in separate tables with an linking ID table between them. Sounds like this table could possibly benefit from some normalization.
If you cant progress that, a view may assist in performance. I am at home without SQL installed so I cant verify any performance aspects.
select personname from yourtablename where personname in ('a','b') group by personname
I actually use this as a screening question for interviews. None of you people would get the job.
OK, maybe you would, but while the strategies you use might or might not work, they aren't generalizable and they miss a basic notion of relational algebra, to wit, aliasing.
The right answer (in the sense that it would make me more likely to employ you as well as the less importance senses that the RDMS's optimizer understands it and it can be extended to other, arbitrarily complex, cases) is
SELECT t1.PersonName
FROM MyTable t1, MyTable t2
WHERE t2.AttributeName = 'CanSpeak'
AND t2.AttributeValue = 1
AND t1.AttributeName = 'IsHuman'
AND t1.AttributeValue = 1
AND t1.PersonName = t2.PersonName;