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.
Related
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
I have Invoice Numbers that are stored as nvarchar(25).
Their Format is ‘####AA’
Where #### is the Invoice Number and AA is the Version Number (partial Order Shipping)
I cannot change the format.
I created two Scalar Functions:
CREATE FUNCTION [dbo].[fnNumbersFromStr](#str varchar(8000))
RETURNS int
AS
BEGIN
WHILE PATINDEX('%[^0-9]%',#str)> 0
SET #str = REPLACE(#str, SUBSTRING(#str, PATINDEX('%[^0-9]%', #str), 1), '')
RETURN CAST(#str AS INT)
END
And it’s brother:
CREATE FUNCTION [dbo].[fnStringFromNum](#str varchar(25))
RETURNS varchar(25)
AS
BEGIN
WHILE PATINDEX('%[^a-z]%',#str)> 0
SET #str = REPLACE(#str, SUBSTRING(#str, PATINDEX('%[^a-z]%', #str), 1), '')
RETURN #str
END
I am stalled with this script:
SELECT
strInvoiceNo,
dbo.fnNumbersFromStr(strInvoiceNo) AS [InvoiceNumber],
dbo.fnStringFromNum(strInvoiceNo) AS [InvoiceString]
FROM #TempTable
Which when runs returns:
strInvoiceNo InvoiceNumber InvoiceString
1000A 1000 A
1000B 1000 B
1000C 1000 C
1001A 1001 A
1001B 1001 B
1002AA 1002 AA
1002AB 1002 AB
1003A 1003 A
1004A 1004 A
I just can’t figure out the next step from here. I am stuck.
I would like the select to only return the latest Invoice Versions:
1000C
1001B
1002AB
1003A
1004A
Sql, Lamda or Linq will work fine for me.
Thanks in advance,
Try this:
SELECT
InvoiceNumber + MAX(InvoiceString) As strInvoiceNo
FROM
(
SELECT
dbo.fnNumbersFromStr(strInvoiceNo) AS [InvoiceNumber],
dbo.fnStringFromNum(strInvoiceNo) AS [InvoiceString]
FROM #TempTable
) As tbl
GROUP BY InvoiceNumber
I dont think you need any UDF for this, a simple windowing function query should return what you looking for.
WITH x AS
(
Select *
,ROW_NUMBER() OVER (PARTITION BY InvoiceNumber ORDER BY strInvoiceNo DESC) rn
FROM TableName
)
SELECT strInvoiceNo, InvoiceNumber, InvoiceString
FROM X
WHERE rn = 1
OR
SELECT strInvoiceNo, InvoiceNumber, InvoiceString
FROM
(
Select *
,ROW_NUMBER() OVER (PARTITION BY InvoiceNumber ORDER BY strInvoiceNo DESC) rn
FROM TableName
)x
WHERE rn = 1
Here is it in LINQ (Assuming fnStringFromNum returns a string padded on the left with spaces):
dbContext.YOURTABLE
.GroupBy(x=>UDFFunctions.fnNumbersFromStr(x.AccountNumber))
.Select(x=>x.OrderByDescending(y=>UDFFunctions.fnStringFromNum(y.AccountNumber).FirstOrDefault())
SQL (using current fnStringFromNum):
SELECT
InvoiceNumber + LTRIM(MAX(RIGHT(SPACE(20)+InvoiceString,20))) As strInvoiceNo
FROM
(
SELECT
dbo.fnNumbersFromStr(strInvoiceNo) AS [InvoiceNumber],
dbo.fnStringFromNum(strInvoiceNo) AS [InvoiceString]
FROM #TempTable
) As tbl
GROUP BY InvoiceNumber
Not necessarily the most efficient, but this will work:
select strInvoiceNo
from #TempTable T
where InvoiceString = (select max(invoicestring) from temp1 where invoicenumber = T.invoicenumber)
order by 1
Edit: Sorry....disregard. This will work off of your full result table but may not be what you actually need. Apologies.
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
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
I have a problem, in example: I have a range of ids of integers (from 1 to 1000), this range supposed to be ids in a SQL Server table, and I want to detect which numbers of this range are not in the table, and sorry for my bad english, thank you
another simpler option would be to use the following query
SELECT number
FROM master..spt_values
WHERE number BETWEEN 1 AND 1000
AND NOT EXISTS ( SELECT 1
FROM Your_Table t --<-- your table where you are checking
WHERE t.ID = number) -- the missing values
GROUP BY number
The above solution is only good if you are looking for around 1000 values. For more values you would need to modify it little bit, something like
-- Select the maximum number or IDs you want to check
DECLARE #Max_Num INT = 10000;
;WITH CTE AS
(
SELECT TOP (#Max_Num) ROW_NUMBER() OVER ( ORDER BY (SELECT NULL)) numbers
FROM master..spt_values v1 cross join master..spt_values v2
)
SELECT c.numbers
FROM CTE c
WHERE NOT EXISTS (SELECT 1
FROM Your_table t
WHERE t.ID = c.numbers)
One way to find "holes" is to generate a list of all possible values and then look for the ones that aren't there. If you can survive with a list of a missing value and then the number of subsequent values following it, you can do this with another method.
SQL Server 2012+ supports lead() and lag(). The following gets almost everything, except for initial missing values:
select t.id + 1 as missingid,
(coalesce(t.nextid, 1000) - t.id - 1) as nummissing
from (select t.*, lead(t.id) over (order by t.id) as nextid
from table t
t.id between 1 and 1000
) t
where t.nextid > t.id + 1 or
(t.nextid is null and t.id <> 1000)
You can get these with a little piece of special logic:
select (case when t.previd is null then 1
else t.id + 1
end) as missingid,
(case when t.previd is null then t.id - 1
else (coalesce(t.nextid, 1000) - t.id - 1)
end) as nummissing
from (select t.*, lead(t.id) over (order by t.id) as nextid,
lag(t.id) over (order by t.id) as previd
from table t
where t.id between 1 and 1000 and
) t
where (t.nextid > t.id + 1 or
(t.nextid is null and t.id <> 1000)
(t.previd is null and t.id <> 1)
)
This sounds like one of the many scenarios where it is helpful to have a numbers table:
SELECT *
FROM lkp_Numbers a
WHERE NOT EXISTS (SELECT 1
FROM YourTable b
WHERE a.num = b.Id)
AND num <= 1000
I use this to create a numbers table:
DROP TABLE lkp_Numbers
DECLARE #RunDate datetime
SET #RunDate=GETDATE()
SELECT TOP 1000 IDENTITY(int,1,1) AS Num
INTO lkp_Numbers
FROM sys.objects s1, sys.objects s2, sys.objects s3
ALTER TABLE lkp_Numbers ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Num)
That method for creating a numbers table was found here: What is the best way to create and populate a numbers table?