Boolean filed on one column - PIVOT table - c#

I am having an issue trying to create a pivot table. I am joining some tables and need to pivot the car model column. For example my tables are:
Person
Id
FirstName
LastName
1
Mary
Ford
2
John
Murphy
3
Cathal
Gibsey
Cars
Id
Description
1
Toyota
2
Ford
3
BMW
4
Hyundaii
5
Volvo
Person-Car
Id
Car
1
1
1
2
1
5
2
3
3
4
3
5
My preferred output would be the description column pivoted as columns:
Id
FirstName
LastName
Toyota
Ford
BMW
Hyundaii
Volvo
1
Mary
Ford
TRUE
TRUE
TRUE
2
John
Murphy
TRUE
3
Cathal
Gibsey
TRUE
TRUE
My Query is:
DECLARE #DynamicPivotQuery AS NVARCHAR(MAX);
DECLARE #Columncars AS NVARCHAR(MAX);
SELECT #Columncars= ISNULL(#Columncars + ',','')
+ QUOTENAME([CDescription])
FROM (
select distinct Description [Columncars]
from Cars
) As Carsssss
select #Columncars
IF OBJECT_ID('tempdb.dbo.##CandidateCarsTable ', 'U') IS NOT NULL
DROP TABLE ##CandidateCarsTable ;
SET #DynamicPivotQuery =
N'Select distinct * into ##CandidateCarsTable
FROM (
select p.firstname, p.lastname, count(c.Id) as''carCount'',
case when c.Description is null then ''N/A'' + N'':car '' else c.Description + N'':car '' end as personCar
from person p
inner join Person-Car pc on pc.Id = p.Id
Inner join cars c on c.Id = pc.Car
PIVOT(MAX(carCount)
FOR [personCar] IN (' + #ColumnCars + ')) AS PVTTable1'
EXEC sp_executesql #DynamicPivotQuery
select * from ##CandidateCarsTable
I cannot seem to get it correct. Any help would be appreciated. Thanks
EDIT, My tables look perfectly find on preview and then change once saved.

This is a static sql. I quess it produces the output you want for example data. The query enumerates all possible person/car pairs and checks if the pair really exists. Change your dynamic sql building code accordingly
Select distinct *
FROM (
select firstname, lastname, [BMW],[Ford],[Hyundaii],[Toyota],[Volvo]
from (
select p.firstname, p.lastname, c.description, case when pc.car is not Null Then N'TRUE' end flag
from person p
cross join cars c
left join PersonCar pc on pc.Id = p.Id and c.Id = pc.Car
) t
PIVOT(MAX(flag) FOR [description] IN ([BMW],[Ford],[Hyundaii],[Toyota],[Volvo])) AS PVTTable1
) t;
Looks like the simpler version will do as unique person.id is included in your last edit.
select id, firstname, lastname, [BMW],[Ford],[Hyundaii],[Toyota],[Volvo]
from (
select p.id, p.firstname, p.lastname, c.description, case when pc.car is not Null Then N'TRUE' end flag
from person p
left join PersonCar pc on pc.Id = p.Id
left join cars c on c.Id = pc.Car
) t
PIVOT(MAX(flag) FOR [description] IN ([BMW],[Ford],[Hyundaii],[Toyota],[Volvo])) AS PVTTable1
db<>fiddle

Related

Use inner join if record exists otherwise use left join

I have the following table structure:
dbo.Owner
OwnerID OwnerName
1 John
2 Marie
3 Alex
and dbo.Pet
PetID PetTag Status OwnerID
1 A341 Active 1
2 A342 Inactive 1
3 A343 Active 2
4 A345 Active 2
I need to return all owners who have only Active pets or no pets.
So in this example above I need to Return Owner 2 (All pets are active) and Owner 3 (No pets)
I will be pulling data in C# using Entity Framework but plain SQL will be sufficient.
Here's what I came up with so far:
select mi.* from Owner o
join Pet p
on o.OwnerID= p.OwnerID
where o.Status='Active'
union select * from Owner
where OwnerID not in (select OwnerID from Pet)
Now, this query above works but it includes OwnerID = 1. and Also I was wondering if there's a way to do this in 1 query without union.
If your only values for Status are "Active" and "Inactive", you can actually simplify your query. When you say:
I need to return all owners who have only Active pets or no pets.
This would then actually translate to:
I need to return all owners who have no Inactive pets.
Then your query becomes much easier.
In an Entity Framework query:
owners = context.Owners
.Where(o => !o.Pets.Any(p => p.Status == "Inactive"))
.ToList();
The SQL query generated by this is:
SELECT
[Extent1].[OwnerID] AS [OwnerID],
[Extent1].[OwnerName] AS [OwnerName]
FROM [dbo].[Owners] AS [Extent1]
WHERE NOT EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Pets] AS [Extent2]
WHERE ([Extent1].[OwnerID] = [Extent2].[OwnerID]) AND (N'Inactive' = [Extent2].[Status])
)
Or to remove the clutter:
SELECT
OwnerID,
OwnerName
FROM Owners o
WHERE NOT EXISTS (SELECT
1
FROM Pets p
WHERE (o.OwnerID = p.OwnerID AND p.Status = 'Inactive')
)
If you have more values for Status, you could use (Entity Framework):
owners = context.Owners
.Where(o => o.Pets.Any(p => p.Status == "Active") || !o.Pets.Any())
.Where(o => !o.Pets.Any(p => p.Status == "Inactive" /* || p.Status == "Lost" and any other values */))
.ToList();
which would generate the SQL query:
SELECT
[Extent1].[OwnerID] AS [OwnerID],
[Extent1].[OwnerName] AS [OwnerName]
FROM [dbo].[Owners] AS [Extent1]
WHERE (( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Pets] AS [Extent2]
WHERE ([Extent1].[OwnerID] = [Extent2].[OwnerID]) AND (N'Active' = [Extent2].[Status])
)) OR ( NOT EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Pets] AS [Extent3]
WHERE [Extent1].[OwnerID] = [Extent3].[OwnerID]
))) AND ( NOT EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Pets] AS [Extent4]
WHERE ([Extent1].[OwnerID] = [Extent4].[OwnerID]) AND (N'Inactive' = [Extent4].[Status])
))
You'd want to test that for performance and there may well be better ways, but it gives the desired result. It does assume you have foreign key/navigation property though.
Try the following query:
select o.*
from dbo.owner o
where not exists(
select *
from dbo.pet p
where p.ownerid=o.ownerid and
p.status='Inactive'
);
SELECT OwnerID, OwnerName
FROM Owner
WHERE OwnerID NOT IN (
SELECT OwnerID from Pet
WHERE Status='Inactive'
This simple query will do the thing.
OwnerId OwnerName
2 Marie
3 Alex
And if you want to select owner with atleast one ACTIVE or NO PET then use the below query.
SELECT o.OwnerID o.OwnerName
FROM Owner o
LEFT JOIN Pet p
ON o.OwnerID= p.OwnerID
AND (p.Status='Active'
OR p.OwnerID is NULL)
OwnerId OwnerName
1 John
2 Marie
3 Alex
This query will return OWNER name until that Owner's all pets are INACTIVE
Now for another case..
If there is a chance for your table to have OwnerId as NULL in Pets Table.
Kindly use the below Query. (Mysql)
SELECT OwnerID, OwnerName
FROM Owner
WHERE OwnerID NOT IN (
SELECT IFNULL(OwnerID,0) from Pet
WHERE Status='Inactive');
ADDED IFNULL() in subquery.
SQLFIDDLE
Interestingly it is possible to do this with a LEFT JOIN. I've no idea about whether this performs differently to the NOT EXISTs queries suggested by other answers.
CREATE TABLE [Owner] (
OwnerID int PRIMARY KEY,
OwnerName nvarchar(50)
);
INSERT INTO [Owner]
VALUES
(1, 'John'),
(2, 'Marie'),
(3, 'Alex');
CREATE TABLE Pet (
PetID int PRIMARY KEY,
PetTag nvarchar(10),
Status nvarchar(30),
OwnerID int FOREIGN KEY REFERENCES [Owner](OwnerID)
);
INSERT INTO Pet
VALUES
(1,'A341','Active', 1),
(2,'A342','Inactive', 1),
(3,'A343','Active', 2),
(4,'A345','Active', 2);
SELECT * FROM [Owner];
SELECT * FROM Pet;
SELECT
o.*
FROM
[Owner] o
LEFT JOIN Pet p
ON o.OwnerID = p.OwnerID
AND p.Status <> 'Active'
WHERE
p.OwnerID IS NULL;
DROP TABLE Pet, [Owner];
select DISTINCT
o.Id
FROM Owner o
LEFT JOIN Pet p ON o.OwnerID= p.OwnerID
where p.Status='Active' OR p.OwnerID IS NULL
SELECT DISTINCT RESULT FROM (
SELECT CASE WHEN POID is NULL
THEN OID
WHEN OID NOT IN (SELECT DISTINCT
OwnerID from Pet
WHERE Status='Inactive')
THEN OID
END AS RESULT
FROM (
SELECT O.OwnerID as OID, P.OwnerID as POID
FROM Owner o
LEFT JOIN Pet p
ON o.OwnerID= p.OwnerID
) T
)T2 WHERE RESULT IS NOT NULL
SQL Fiddle
Interesting that although you tagged it entity-framework, most answers don't come up with the simplifications that entity-framework offers you.
There is a one-to-many relationship between Owners and Pets. Every Owner has zero or more Pets, every Pet belongs to exactly one Owner.
If you have configured your entity framework classes correctly for a one-to-many relationship, they will be like:
class Owner
{
public int Id {get; set;}
// every Owner has zero or more Pets:
public virtual ICollection<Pet> Pets {get; set;}
... // other properties
}
class Pet
{
public int Id {get; set;}
// every Pet belongs to exactly one Owner, using foreign key:
public int OwnerId {get; set;}
public Owner Owner {get; set;}
}
class MyDbConnection : DbConnection
{
public DbSet<Owner> Owners {get; set;}
public DbSet<Pet> Pets {get; set;}
}
This is enough for entity framework to recognize that you designed the one-to-many relationship. Whenever needed, entity framework will do the correct join for you.
I need to return all owners who have only Active pets or no pets.
This is the same collections as:
I need to return all owners who don't have Inactive pets
(note to self: Owners without Pets, surely don't have Inactive Pets!)
If you've set up the classes correctly, the queries will be much more readable. You can think of collections, instead of tables that are related towards each other using IDs
using (var dbConnection = new MyDbConnection())
{
var requestedOwners = dbConnection.Owners // Give me all Owners
.Where(owner => !owner.Pets.Any() // that have no Pets at all
|| owner.Pets.All(pet => pet.Status == Active)); // or have only active Pets
}
Entity Framework will recognize that this needs a join and will translate this into the correct inner joins for you.
The second query is even simpler, and probably faster because you can continue to the next Owner as soon as an Inactive Pet is found
var ownersWithoutInactivePets = dbContext.Owners // give me all Owners
.Where(owner => !owner.Pets // that don't have
.Any(pet => pet.Status == Inactive); // any inactive Pets
Again, entity framework will do the join for you

How can I retrieve data from joined tables?

I have 3 tables:
products table
--------------
id
name
...
categories table
----------------
id
name
...
product_categories table
------------------------
product_id
category_id
And join them by this query:
select p.*
from products p
join product_categories pc on pc.product_id = p.id
join categories c on pc.category_id = c.id
This query return multiple records of a product per each category of that product, but I want to get only one product and multiple category of that product.
This is my output:
p.Id p.name --> cat_id cat_name
1 product_1 --> 1 cat1
1 product_1 --> 3 cat3
1 product_1 --> 2 cat2
1 product_1 --> 6 cat6
2 product_2 --> 5 cat5
2 product_2 --> 1 cat1
.
.
.
And desired output:
p.Id p.name --> cat_id cat_name,cat_id cat_name,...
1 product_1 --> 1 cat1,3 cat3,2 cat2,6 cat6
2 product_2 --> 5 cat5,1 cat1
.
.
.
How can I do that?
You should add WHERE clause to specify ID and in select clause maybe you want to use c.* instead of p.* to get all data from categories.
select p.name, c.*
from products p
join product_categories pc on pc.product_id = p.id
join categories c on pc.category_id = c.id
where p.id = --smth
If you don't want the redundancy of the product records, in other words you only want the category information and nothing else, then you don't need the join to the main products table in the first place (assuming you're using the product id).
However, you're problem is that unless you're planning to display a column for every possible category and whether the product has it or not then you're going to need to do a work around. My preferred approach would be to create a cursor that runs through each row and pulls the categories for a given product id into a single string and puts this string into a new table (product id, categories).
DECLARE #productId int
DECLARE #prevProductId int
SET #prevProductId = 0
DECLARE #cid int
DECLARE #cname nvarchar(255)
DECLARE #categoryList nvarchar(max)
SET #categoryList = ''
DECLARE #ProdCatTable TABLE
(
ProductId int,
Categories nvarchar(max)
)
DECLARE category_cursor CURSOR FOR
SELECT pc.product_id, c.id, c.name
FROM categories c
JOIN product_categories pc
ON pc.category_id = c.id
ORDER BY pc.product_id
OPEN category_cursor
FETCH NEXT FROM category_cursor
INTO #productId, #cid, #cname
WHILE ##FETCH_STATUS = 0
BEGIN
IF #prevProductId = 0
BEGIN
SET #categoryList = #cid + ' ' + #cname
SET #prevProductId = #productId
END
ELSE IF (#prevProductId <> #productId) AND #categoryList <> ''
INSERT INTO #ProdCatTable (ProductId, Categories)
VALUES (#prevProductId, #categoryList)
SET #prevProductId = #ProductId
SET #categoryList = ''
ELSE
BEGIN
SET #categoryList = #categoryList + ', ' + #cid + ' ' + #cname
END
FETCH NEXT FROM category_cursor
INTO #productId, #cid, #cname
END
CLOSE category_cursor
DEALLOCATE category_cursor
SELECT ProductId, Catgories
FROM #ProdCatTable
NOTE: This hasn't been tested, but I think it should work if you're happy to return a table with the product id as your identify and the categories as a string.
SELECT Tbl_Emp.EMPId, Tbl_Customer.First_Name, Tbl_Mail_IDs.MailId
FROM Tbl_Emp INNER JOIN
Tbl_Mail_IDs ON Tbl_Emp.EMPId = Tbl_Mail_IDs.Id INNER
JOIN
Tbl_Customer ON Tbl_Mail_IDs.Id = Tbl_Customer.Customer_Id

sql query with complicated join to get unique records list

I need a query that select customer table with right cardId by applied the below cases.
If you have any suggestions, please share.
Possible cases are:
Only one records found - easy, use the Card_id found
No records found - leave blank
More than one record found - use the Card_id that starts with 2000 if available, otherwise pick the one with latest created date (in CustomerCards table)
Customer Table:
ID CardID
1 200132
2 263987
3 100789
..
CustomerCards table
CustomerId CardID CreatedOn
1 209890 12/11/2014
1 200132 12/12/2014
1 100732 11/10/2014
2 168902 12/11/2014
2 263987 15/01/2015
I've started with left join:
select ct.* from dbo.Customer ct
left join dbo.CustomerCard cc
on ct.id = cc.customerId
And a bit stuck after that.
A start
;with cte1 as
(
select cc.CustomerId, cc.CardID, cc.CreatedOn
from dbo.CustomerCard cc
group by cc.CustomerId, cc.CardID, cc.CreatedOn
having count(*) = 1
), cte200 as
(
select cc.CustomerId, cc.CardID, max(cc.CreatedOn)
from dbo.CustomerCard cc
group by cc.CustomerId, cc.CardID
where cc.CardID like '2000%'
)
select cte1
union
select cte2000
union
select ct.ID, ct.CardID, '1/1/1900' as CreatedOn
from dbo.Customer ct
left join dbo.CustomerCard cc
on ct.id = cc.customerId
where cc.customerId is null
union
select cc.ID, cc.CardID, max(cc.CreatedOn)
from dbo.CustomerCard cc
left join cte1
on cte1.customerId = cc.customerId
left join cte2000
on cte2000.customerId = cc.customerId
where cte1.customerId is null
and cte2000.customerId is null
group by cc.ID, cc.CardID

linq multible join does not work in some empty tables

hi I'm using 4 tables..
Sometimes it does not have information in the this tables(education or address or certificate)
so the results are coming empty..
If have information how can I say?
example
**PersonTable**
PersonId PersonName
1453 george
**Certificate Table**
CertificateId PersonId CertificateName ( 0 records)
**Addres Table**
AddresId PersonId Adress
1 1453 ......
2 1453 .....
3 1453 ......
4 1453 ......
5 1453 ........
**EducationTable**
EducationId PersonId Education
1 1453 school name
**BankTable**
BankId PersonId Bank ( 0 records )
Person table = 1 records
Certificates = 0 records
Address = 5 records
Education = 1 records
Bank = 0 records
var query = (from p in DbEntities.t_Person
join q in DbEntities.t_Address on p.PersonId equals q.addressId
join n in DbEntities.t_Education on p.PersonId equals n.EduId
join j in DbEntities.t_Certificate on p.PersonId equals j.CertificateId
join f in DbEntities.t_BankAccount on p.PersonId equals f.BankId
where p.PersonId.Equals(id)
select new { p, q, n, j, f }).FirstOrDefault();
return query values null ,
I do not see why these values​​?
Person table = 1 records
Address = 5 records
Education = 1 records
I thought about it, but did not
var query = (from p in DbEntities.t_Person
where p.PersonId.equlas(id)
select p).singleOrDefault();
query = from p in dbEntities.Address
WHERE P.AddressId.equlas(query.PersonId)
select p;
It's because you're doing an INNER JOIN, which will only return records if all join conditions are met! It looks like you want to do a LEFT JOIN, which will return all rows from the left table, and from the right table it will return any record that is found, and null otherwise. Here is a good post explaining the different types of joins, with diagrams!
The Linq-to-Entities way to do a left join is DefaultIfEmpty(). See MSDN for a usage example.

Dynamic SQL Server Query to Fill DataTable

Hello I need to do the follow query in SQL Server to fill and Datatable.
In my case I have 3 tables:
1 - Users
2 - Process
3 - Status
Users
id, name
Process
id, cod_user, cod_status
Status
id, status
I need to make an query that return the follow table:
User.Name | Status.Created | Status.Opened | Status.Finalized
Tom 50 30 20
Roger 22 33 44
Kris 11 09 05
And then, I have to return this table to fill and datatable.
Thanks...
Looks like you are trying to PIVOT the data, you can use something like this if you know the values of status to transform:
select name, [Created], [Opened], [Finalized]
from
(
select u.name,
s.status
from users u
left join process p
on u.id = p.cod_user
left join status s
on p.cod_status = s.id
) src
pivot
(
count(status)
for status in ([Created], [Opened], [Finalized])
) piv
If you have an unknown number of statuses to turn into columns, then you can use dynamic sql:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(status)
from status
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT name, ' + #cols + ' from
(
select u.name,
s.status
from users u
left join process p
on u.id = p.cod_user
left join status s
on p.cod_status = s.id
) x
pivot
(
count(status)
for status in (' + #cols + ')
) p '
execute(#query)
If you don't have access to the PIVOT function, then you can replicate it using an aggregate function and a CASE statement:
select u.name,
sum(case when s.status = 'created' then 1 else 0 end) created,
sum(case when s.status = 'opened' then 1 else 0 end) opened,
sum(case when s.status = 'finalized' then 1 else 0 end) finalized
from users u
left join process p
on u.id = p.cod_user
left join status s
on p.cod_status = s.id
group by u.name
Looks like you're trying to show one record per User, with one column per Status, and the columns contain the count of records in the Process table with that status for that user?
If that's correct, then you want to use a Pivot Table.

Categories