I have two tables: tblSizes and tblColors. tblColors has columns called ColorName, ColorPrice and SizeID. There is one size to multiple colors. I need to write a query to select the size and all the colors (as columns) for a that size with the price of each size in its respective column.
The colors must be returned as columns, for instance:
SizeID : Width : Height : Red : Green : Blue
1---------220-----220----£15----£20-----£29
Hope this makes sense
Thank you
Edit: Tried the following code but not quite sure what's wrong:
DECLARE #Colors NVARCHAR(4000), #Query NVARCHAR(MAX)
SET #Colors = ''
SELECT #Colors = #Colors + '[' + C.Color +'],'
FROM tblTempProductSizesColors SC
INNER JOIN tblColors C on SC.ColorID=C.ID
GROUP BY Color
ORDER BY Color
SET #Colors = LEFT(#Colors,LEN(#Colors)-1)
SET #Query = '
SELECT *
FROM ( SELECT TS.Sizeid, TS.Width, TS.Height, TS.Depth, TC.Price
FROM tblTempProductSizes TS
INNER JOIN tblTempProductSizesColors TC
ON TS.SizeId = TC.SizeId INNER JOIN tblColors C on TC.ColorID=C.ID) A
PIVOT(SUM(Price) FOR C.Color IN ('+#Colors+')) AS PT'
EXEC sp_executesql #Query
select s.Sizeid, s.Width, s.Height,
Red = SUM(CASE WHEN c.ColorName = "Red" THEN c.ColorPrice ELSE 0 END),
Blue = SUM(CASE WHEN c.ColorName = "Blue" THEN c.ColorPrice ELSE 0 END),
Green = SUM(CASE WHEN c.ColorName = "Green" THEN c.ColorPrice ELSE 0 END)
from tblSizes s
join tblColors c on c.SizeId = s.SizeId
group by s.Sizeid, s.Width, s.Height
If you don't want to write every color, and the expression for those columns, you can use Dynamic SQL if you have MSSQL 2005+ (first, be sure of take a look to this link).
UPDATED following comment
DECLARE #Colors NVARCHAR(4000), #Query NVARCHAR(MAX)
SET #Colors = ''
SELECT #Colors = #Colors + '[' + C.Color +'],'
FROM tblTempProductSizesColors SC
INNER JOIN tblColors C on SC.ColorID=C.ID
GROUP BY Color
ORDER BY Color
SET #Colors = LEFT(#Colors,LEN(#Colors)-1)
SET #Query = '
SELECT *
FROM ( SELECT TS.Sizeid, TS.Width, TS.Height, TS.Depth, TC.Price, C.Color
FROM tblTempProductSizes TS
INNER JOIN tblTempProductSizesColors TC
ON TS.SizeId = TC.SizeId INNER JOIN tblColors C on TC.ColorID=C.ID) A
PIVOT(SUM(Price) FOR Color IN ('+#Colors+')) AS PT'
EXEC sp_executesql #Query
Related
I'm using a SQL Server, I've a query which return the data of all the fields, The main thing is that 1 field can belongs to multiple records, the record ID differentiate them.
I've a data set like this.
This is my current data set
My current query:
Select fd.FieldName ,FV.FieldID, Data , R.RecordID from FieldValues FV
Inner Join Records R on R.RecordID = FV.RecordID
Inner Join Forms F On f.FormID = R.FormID
Inner join Fields fd on fd.FieldID = fv.FieldID
Where R.RecordID IN (45,46)
I need to create 1 row of each columns that belongs to the same RecordID like this.
Service Name Location city VendorCode RecordID
Raj ABC LOCATION ABC CITY 32 45
BEN ABC LOCATION ABC CITY -- 46
The above is my desired output.
I've tried with pivot but have not succeeded.
If you don't like to deal with dynamic pivot and you do know the key of the rows you want to convert into columns, you can use standard sql with max and case when
select
max(case fd.FieldName when 'SelectService' then Data else null end) as ServiceName,
max(case fd.FieldName when 'EnterYourLocation' then Data else null end) as Location,
max(case fd.FieldName when 'City' then Data else null end) as city,
max(case fd.FieldName when 'VendorCodeOption' then Data else null end) as VendorCode,
R.RecordId
from FieldValues FV
Inner Join Records R on R.RecordID = FV.RecordID
Inner Join Forms F On f.FormID = R.FormID
Inner join Fields fd on fd.FieldID = fv.FieldID
where R.RecordID IN (45,46)
group by R.RecordId
This is the solution with pivot but it is missing to include adjust joins
declare #columns varchar(max) set #columns = ''
select #columns = coalesce(#columns + '[' + cast(col as varchar(MAX)) + '],', '')
FROM ( select FieldName as col from FieldValues group by FieldName ) m
set #columns = left(#columns,LEN(#columns)-1)
DECLARE #SQLString nvarchar(max);
set #SQLString = '
select * from
( select RecordId, FieldName, Data from FieldValues) m
PIVOT
( MAX(Data)
FOR FieldName in (' + #columns + ')
) AS PVT'
EXECUTE sp_executesql #SQLString
This is my sample code:
SQL Fiddle
I need it to result like this:
category outlet1 outlet2 outlet3
Sale 70 20 40
Expense 250 130 200
How can I do this?
EDIT: My outlets are not fixed, sorry for not telling this beforehand.
You can solve your particular problem using conditional aggregation:
SELECT c.category,
SUM(CASE WHEN o.outletname = 'Outlet1' THEN t.amt ELSE 0 END) as Outlet1,
SUM(CASE WHEN o.outletname = 'Outlet2' THEN t.amt ELSE 0 END) as Outlet2,
SUM(CASE WHEN o.outletname = 'Outlet3' THEN t.amt ELSE 0 END) as Outlet3
FROM tblcategory c INNER JOIN
tbltran t
ON t.catid = c.id INNER JOIN
tbloutlet o
ON o.id = t.outletid
GROUP BY c.category;
If the outlet names are not fixed, then you need dynamic SQL. The problem cannot be solve using a single SELECT query.
Here with dynamic Outlets http://sqlfiddle.com/#!18/a7b09/25
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(outletname)
from tbloutlet
group by outletname
order by outletname
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT category,' + #cols + ' from
(
SELECT c.category, o.outletname, SUM(t.amt) as amt
FROM tblcategory c
INNER JOIN tbltran t ON t.catid = c.id
INNER JOIN tbloutlet o ON o.id = t.outletid
GROUP BY c.category, o.outletname
) x
pivot
(
sum(amt)
for outletname in (' + #cols + ')
) p '
execute(#query);
you may also use the PIVOT operator
SELECT *
FROM
(
SELECT category, outletname, amt
FROM tblcategory c
INNER JOIN tbltran t ON t.catid = c.id
INNER JOIN tbloutlet o ON o.id = t.outletid
) d
PIVOT
(
SUM(amt)
FOR outletname in ([Outlet1] , [Outlet2] , [Outlet3])
) p
EDIT : below is the Dynamic SQL version
declare #Outlets nvarchar(max),
#SQL nvarchar(max)
select #Outlets = isnull(#Outlets + ',', '') + quotename(outlet)
from outlet
select #SQL = '
SELECT *
FROM
(
SELECT category, outletname, amt
FROM tblcategory c
INNER JOIN tbltran t ON t.catid = c.id
INNER JOIN tbloutlet o ON o.id = t.outletid
) d
PIVOT
(
SUM(amt)
FOR outletname in (' + #Outlets + ')
) p'
print #SQL -- print out for verification
exec sp_executesql #SQL
I have 3 tables:
tUsers
-uid
tColors
-colorid
-colorname
tColors_User_Detail
-uid_fk
-colorid_fk
Users select which colors they like, and only the colors they like. This creates records in tColors_User_Detail. I need to flatten this out, so that each user has one record with the color from tColors as a column name, and they have a True/False value in the row for each color depending on if they had a record in tColors_User_Detail. If the user did not have a color selected in tColors_User_Detail, it would be a False value in the specific color column. And, if they do have a record in tColors_User_Detail for a color, it would be a true value for the corresponding color column.
Any help appreciated.
Here's a basic PIVOT example with a COALESCE to show 'false' if no value is available. This assumes you have to hard-code the names of the colors for the column names.
DECLARE #tUsers TABLE ([uid] INT)
DECLARE #tColors TABLE ([colorid] INT, [colorname] VARCHAR(50))
DECLARE #tColors_User_Detail TABLE ([uid_fk] INT, [colorid_fk] INT)
INSERT #tUsers VALUES (1),(2)
INSERT #tColors VALUES (1,'Blue'),(2,'Red'),(3,'Green')
INSERT #tColors_User_Detail VALUES (1,1),(1,2),(1,3),(2,1)
SELECT
uid,
COALESCE([Red], 'False') AS [Red],
COALESCE([Blue], 'False') AS [Blue],
COALESCE([Green], 'False') AS [Green]
FROM #tUsers U
LEFT OUTER JOIN #tColors_User_Detail CUD
ON CUD.uid_fk = U.uid
LEFT OUTER JOIN #tColors C
ON C.colorid = CUD.colorid_fk
PIVOT (MAX(colorname) FOR colorname IN (
[Red],
[Blue],
[Green]
)) PVT
If you want to let the columns be dynamic from the colors, you'll have to use dynamic sql.
DECLARE #Sql VARCHAR(1000) =
'SELECT uid'
+ (SELECT ', CASE WHEN [' + [colorname] + '] IS NOT NULL THEN ''True'' ELSE ''False'' END AS [' + [colorname] + ']' AS [text()] FROM tColors FOR XML PATH(''))
+ ' FROM tUsers U
LEFT OUTER JOIN tColors_User_Detail CUD
ON CUD.uid_fk = U.uid
LEFT OUTER JOIN tColors C
ON C.colorid = CUD.colorid_fk
PIVOT (MAX(colorname) FOR colorname IN ('
+ SUBSTRING((SELECT ',[' + [colorname] + ']' AS [text()] FROM tColors FOR XML PATH('')), 2, 1000)
+ ')) PVT'
EXEC (#Sql)
What flavor of SQL?
Something along the lines of:
http://sqlfiddle.com/#!6/ec4e2
SELECT U.uid
, C.colorid
, C.colorname
, ( CASE WHEN cud.uid_fk IS NOT NULL THEN 'True' ELSE 'False' END ) AS ColorChosen
FROM tUsers U
FULL OUTER JOIN tColors C ON 1=1
LEFT OUTER JOIN tColors_User_Detail cud ON
U.uid = cud.uid_fk
AND C.colorid = cud.colorID_FK
EDIT: I missed the pivot for one row per user. Meeting time though. Be back in a bit.
Below code gives me result set as shown in first image
SELECT dbo.tbStudent.Name, dbo.tbStudent.RegNo, dbo.tbFee.PID, dbo.tbFee.Purpose, dbo.tbFee.AmountPaid, dbo.tbFee.StudentID, dbo.tbFee.Date, dbo.tbFee.FeeID,
dbo.tbFee.SemID, dbo.tbFee.CourseID, dbo.tbFee.ModeOfPayment, dbo.tbFee.CheckNo, dbo.tbFee.DDNo, dbo.tbFee.HostelDDNo, dbo.tbFee.FRID,
dbo.tbStudent.Parentage, dbo.tbCourse.Name AS Course, ISNULL(dbo.tbSemester.SemName, ' + #st +') AS Semester
FROM dbo.tbFee INNER JOIN
dbo.tbStudent ON dbo.tbFee.StudentID = dbo.tbStudent.StudentID INNER JOIN
dbo.tbCourse ON dbo.tbFee.CourseID = dbo.tbCourse.CourseID LEFT OUTER JOIN
dbo.tbSemester ON dbo.tbFee.SemID = dbo.tbSemester.SemID Where tbFee.SemID=1
However using Pivot table I need result as below:
My code for pivot table is :
SET #values = '';
If(#SemID=0)
BEGIN
SELECT #values = #values +'['+ CAST(PurPose AS varchar(max))+ ']' + ','
FROM tbFee Where CourseID=#CourseID
SET #values = SUBSTRING(#values, 1, Len(#values) - 1)
END
ELSE
BEGIN
SELECT #values = #values +'['+ CAST(PurPose AS varchar(max))+ ']' + ','
FROM tbFee Where SemID=#SemID
SET #values = SUBSTRING(#values, 1, Len(#values) - 1)
END
Declare #st nvarchar(max)
set #st='''Not Available''';
declare #q nvarchar(max)
set #q = '
Select * from(
SELECT dbo.tbStudent.Name, dbo.tbStudent.RegNo, dbo.tbFee.PID, dbo.tbFee.Purpose, dbo.tbFee.AmountPaid, dbo.tbFee.StudentID, dbo.tbFee.Date, dbo.tbFee.FeeID,
dbo.tbFee.SemID, dbo.tbFee.CourseID, dbo.tbFee.ModeOfPayment, dbo.tbFee.CheckNo, dbo.tbFee.DDNo, dbo.tbFee.HostelDDNo, dbo.tbFee.FRID,
dbo.tbStudent.Parentage, dbo.tbCourse.Name AS Course, ISNULL(dbo.tbSemester.SemName, ' + #st +') AS Semester
FROM dbo.tbFee INNER JOIN
dbo.tbStudent ON dbo.tbFee.StudentID = dbo.tbStudent.StudentID INNER JOIN
dbo.tbCourse ON dbo.tbFee.CourseID = dbo.tbCourse.CourseID LEFT OUTER JOIN
dbo.tbSemester ON dbo.tbFee.SemID = dbo.tbSemester.SemID Where tbFee.SemID=1
) as x
pivot (
max(AmountPaid)
for Purpose in (' + #values + ')
) as pvt
'
exec (#q)
I am getting Values of Purpose columns in #values due to the reason that number of rows can change. However instead of getting result as single row for same student having same regNo , I am getting below result :
But what I am getting is below:
In the source query for your PIVOT, you should only specify those columns which are involved in the actual pivot - namely dbo.tbStudent.Name, dbo.tbStudent.RegNo, dbo.tbFee.Purpose, dbo.tbFee.AmountPaid.
SELECT
dbo.tbStudent.Name,
dbo.tbStudent.RegNo,
dbo.tbFee.Purpose,
dbo.tbFee.AmountPaid
FROM
dbo.tbFee
INNER JOIN dbo.tbStudent ON dbo.tbFee.StudentID = dbo.tbStudent.StudentID
INNER JOIN dbo.tbCourse ON dbo.tbFee.CourseID = dbo.tbCourse.CourseID
LEFT OUTER JOIN dbo.tbSemester ON dbo.tbFee.SemID = dbo.tbSemester.SemID
Where tbFee.SemID=1
If any other columns apart from these are present, they will be factored into the pivot computation, and you will get multiple rows accordingly.
How can I create a generic function in C# (LINQ-to-SQL) or SQL that takes two tables with matching structure and counts how many rows in TableA that are in TableB.
TableA Structure
Value1, Value2
TableA Data
1,1
TableB Structure
Value1, Value2
TableB Data
1,1,
1,2
To get count of matching rows between TableA and TableB:
SELECT COUNT(*) FROM TableA
INNER JOIN TableB ON
TableA.Value1 = TableB.Value1 AND
TableA.Value2 = TableB.Value2
The result in this example
1
So the query above works great, but I don't want to have to write a version of it for every pair of tables I want to do this for since the INNER JOIN is on every field. I feel like there should be a more generic way to do this instead having to manually type out all of the join conditions. Thoughts?
Edit: Actually, I think I'll need a C#/LINQ answer since this will be done across servers. Again, this is annoying because I have to write the code to compare each field manually.
var tableARows = dbA.TableA.ToList();
var tableBRows = dbB.TableB.ToList();
var match = 0;
foreach(tableARow in tableARows){
if(tableBRows.Where(a=>a.Value1 = tableARow.Value1 && a.Value2 = tableARow.Value2).Any()){
match++;
}
}
return match;
using sql server this will work
var sql="select count(0) from(
select * from product except select * from product1
) as aa";
dc = dtataContext
var match= dc.ExecuteStoreQuery<int>(sql);
You could generate the join using syscolumns.
declare #tablenameA varchar(50) = 'tableA'
declare #tablenameB varchar(50) = 'tableB'
declare #sql nvarchar(4000)
select #sql =''
select #sql = #sql + ' and ' + quotename(#tablenameA )+ '.'
+ c.name +' = ' + quotename(#tablenameB )+ '.' + c.name
from syscolumns c
inner join sysobjects o on c.id = o.id
where o.name = #tablenameA
select #sql = 'select count(*) from ' + #tablenameA + ' inner join '+#tablenameB+' on '
+ substring (#sql, 5, len(#sql))
exec sp_executesql #sql
You query the ANSI INFORMATION_SCHEMA views, thus:
select *
from INFORMATION_SCHEMA.COLUMNS col
where col.TABLE_SCHEMA = <your-schema-name-here>
and col.TABLE_NAME = <your-table-name-here>
order by col.ORDINAL_POSITION
against each of the tables involved. The result set will provide everything needed for your to construct the required query on the fly.
One fairly simple answer:
select ( select count(*) from
(select * from TableA UNION ALL select * from TableB) a ) -
( select count(*) from
(select * from TableA UNION select * from TableB) d ) duplicates