SQL Count Report Query - sql

I have following table in SQL Server.
Test.
id | Author| Ext
----+-------+--------
01 | Bill | txt
02 | Tom | pdf
03 | Bill | doc
04 | Alex | txt
05 | Alex | pdf
06 | Tom | pdf
I want following to be output based on id.
| Author| txt | pdf | doc
--------+-----+-----+-----
| Bill | 1 | 0 | 1
| Tom | 0 | 2 | 0
| Alex | 1 | 1 | 0
Can anyone suggest me query for the same?

Use CASE expression.
Query
select [Author],
sum(case [Ext] when 'txt' then 1 else 0 end) as [txt],
sum(case [Ext] when 'pdf' then 1 else 0 end) as [pdf],
sum(case [Ext] when 'doc' then 1 else 0 end) as [doc]
from [your_table_name]
group by [Author];

If you want Ext column value to be dynamic, you can use dynamic query string.
Example :
CREATE TABLE Table1
(id int, author varchar(100), ext varchar(3));
INSERT INTO Table1
(id, author, ext)
VALUES
(1, 'Bill', 'txt'),
(2, 'Tom', 'pdf'),
(3, 'Bill', 'doc'),
(4, 'Alex', 'txt'),
(5, 'Alex', 'pdf'),
(6, 'Tom', 'pdf')
Query 1:
DECLARE #MyCursor CURSOR
DECLARE #SQLString NVARCHAR(MAX)
DECLARE #MyField VARCHAR(30)
SET #SQLString = 'SELECT author '
SET #MyCursor = CURSOR FOR
SELECT ext FROM Table1 group by ext
OPEN #MyCursor
FETCH NEXT FROM #MyCursor
INTO #MyField
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQLString = #SQLString + ',sum(case [ext] when ''' + #MyField + ''' then 1 else 0 end) as ' + #MyField
FETCH NEXT FROM #MyCursor
INTO #MyField
END
CLOSE #MyCursor
DEALLOCATE #MyCursor
SET #SQLString = #SQLString + ' FROM Table1 group by author'
EXECUTE sp_executesql #SQLString
Results:
| author | doc | pdf | txt |
|--------|-----|-----|-----|
| Alex | 0 | 1 | 1 |
| Bill | 1 | 0 | 1 |
| Tom | 0 | 2 | 0 |

Use below pivot method :
CREATE TABLE #Temp ( id VARCHAR(10) , Author VARCHAR(10), Ext VARCHAR(10) )
INSERT INTO #Temp ( id , Author , Ext )
SELECT 01,'Bill','txt' UNION ALL
SELECT 02,'Tom','pdf' UNION ALL
SELECT 03,'Bill','doc' UNION ALL
SELECT 04,'Alex','txt' UNION ALL
SELECT 05,'Alex','pdf' UNION ALL
SELECT 06,'Tom','pdf'
SELECT *
FROM
(
SELECT Author , Ext
FROM #Temp
) A
PIVOT
(
COUNT(Ext) FOR Ext IN ([txt],[pdf],[doc])
) A

Using PIVOT
with a as
(select 1 as a_id,'abc' as author,'txt' as ext from dual
union
select 2 ,'pqr' as author,'pdf' as ext from dual
union
select 3,'abc' as author,'doc' as ext from dual
union
select 4,'lmn' as author,'txt' as ext from dual
union
select 5,'pqr' as author,'pdf' as ext from dual
)
select *
from
( select *
from a
) pivot
( count(ext)
for ext in
('txt' as txt_ext,'pdf' as pdf_ext,'doc' as doc_ext)
);

For this option, i think using pivot is the best way;
Select * FROM(
Select Authot,Ext,Count(Ext) CountExt FROM #temp Group By Authot,Ext
) t
PIVOT(
SUM(CountExt)
FOR EXt IN ([pdf],[txt],[doc])) as pvt

CASE expression should be used as follow:
SELECT
author,
COUNT(CASE WHEN ext = 'txt' THEN 1 END) as txt,
COUNT(CASE WHEN ext = 'pdf' THEN 1 END) as pdf,
COUNT(CASE WHEN ext = 'doc' THEN 1 END) as doc
FROM myTable
GROUP BY author

Related

How can I build a utility matrix table in microsoft SQL Server?

| Store_ID | item |
+ ----------+----------+
| 6 | Soda |
| 8 | Chips |
| 9 | Candy |
| 9 | Soda |
I basically have the above table. I want to make Store_id the rows and item the columns and have a flag as the values of the table. This is basically a user-interactions matrix/utility matrix.
How can I convert this Table to another Table of the aforementioned form?
Output:
store_id soda chips candy
-------------------------
6 1 0 0
8 0 1 0
9 1 0 1
One approach is to use a dynamic pivot table. Find an example below:
--- QUERY ---
-- Build list of unique item names
-- CAUTION: Consider using a domain table instead to retrieve the unique item list for performance reasons in case the store table is huge.
DECLARE #Columns AS VARCHAR(MAX)
SELECT
#Columns = COALESCE(#Columns + ', ','') + QUOTENAME(item)
FROM
(SELECT DISTINCT item FROM store) AS B
ORDER BY
B.item
-- Build SQL query
DECLARE #SQL AS VARCHAR(MAX)
SET #SQL = 'SELECT store_id, ' + #Columns + '
FROM
(
SELECT store_id, item
FROM store
) as PivotData
PIVOT
(
COUNT(item)
FOR item IN (' + #Columns + ')
) AS PivotResult
ORDER BY store_id';
-- Execute query
EXEC(#SQL)
--- RESULT ---
store_id Candy Chips Soda
----------- ----------- ----------- -----------
6 0 0 1
8 0 1 0
9 1 0 1
(3 rows affected)
Tested on Microsoft SQL Server 2019 (RTM-GDR) (KB4517790) - 15.0.2070.41 (X64)
wit that table design I only come with this solution
with stores as (
select Store_ID = 6, item = 'soda'
union all
select 8, 'candy'
union all
select 9, 'candy'
union all
select 9, 'soda'
union all
select 9, 'candy'
union all
select 9, 'soda'
union all
select 1, 'chips')
select store_id, soda = SUM(CASE WHEN item = 'soda' then 1 else 0 end),
candy = SUM(CASE WHEN item = 'candy' then 1 else 0 end),
chips = SUM(CASE WHEN item = 'chips' then 1 else 0 end)
from stores
group by store_id, item

How to combine multiple SQL rows into columns dynamically

I have a table with
+-------+-------+-----------------+
| P1_ID | P2_ID | Relationship_ID |
+-------+-------+-----------------+
| 1 | 21 | 3 |
| 1 | 32 | 3 |
| 2 | 45 | 2 |
| 2 | 65 | 1 |
| 3 | 98 | 3 |
| 3 | 94 | 4 |
+-------+-------+-----------------+
I want the final table to look like:
+-------+--------+--------+------+------+
| P1_ID | P2_ID1 | P2_ID2 | RID1 | RID2 |
+-------+--------+--------+------+------+
| 1 | 21 | 32 | 3 | 3 |
| 2 | 45 | 65 | 2 | 1 |
| 3 | 98 | 94 | 3 | 4 |
+-------+--------+--------+------+------+
I am not sure which direction to go with this. I am trying to use a pivot but I can not seem to make it work. Maybe I am doing it wrong.
You can use conditional aggregation for this. This isn't exactly what you stated for output because the ordering of your data is a little funky. But this should point you in the right direction.
declare #Something table
(
P1_ID int
, P2_ID int
, Realationship_ID int
)
insert #Something values
(1,21,3)
, (1,32,3)
, (2,45,2)
, (2,65,1)
, (3,98,3)
, (3,94,4)
select P1_ID
, P2_ID1 = MAX(Case when RowNum = 1 then P2_ID end)
, P2_ID2 = max(case when RowNum = 2 then P2_ID end)
, RID1 = MAX(Case when RowNum = 1 then Realationship_ID end)
, RID2 = max(case when RowNum = 2 then Realationship_ID end)
from
(
select *
, RowNum = ROW_NUMBER() over(partition by s.P1_ID order by Realationship_ID)
from #Something s
) x
group by x.P1_ID
--EDIT--
Here is a fully dynamic solution for this. I switched to using a temp table because a table variable would be out of scope for dynamic sql. Obviously in your situation you would be using a persistent table. This will order the output by P1_ID and the columns within each row by Realationship_ID.
if OBJECT_ID('tempdb..#Something') is not null
drop table #Something
create table #Something
(
P1_ID int
, P2_ID int
, Realationship_ID int
)
insert #Something values
(1,21,3)
, (1,32,3)
, (2,45,2)
, (2,65,1)
, (3,98,3)
, (3,94,4)
;
declare #DynamicPortion nvarchar(max) = '';
declare #FinalStaticPortion nvarchar(2000) = ' from OrderedResults Group by P1_ID order by P1_ID';
declare #StaticPortion nvarchar(2000) =
'with OrderedResults as
(
select *
, RowNum = ROW_NUMBER() over(partition by s.P1_ID order by Realationship_ID)
from #Something s
)
select P1_ID';
with E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a cross join E1 b), --10E+2 or 100 rows
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E2
)
select #DynamicPortion = #DynamicPortion +
', MAX(Case when RowNum = ' + CAST(N as varchar(6)) + ' then P2_ID end) as P2_ID' + CAST(N as varchar(6)) + CHAR(10) +
', MAX(Case when RowNum = ' + CAST(N as varchar(6)) + ' then Realationship_ID end) as RID' + CAST(N as varchar(6)) + CHAR(10)
from cteTally t
where t.N <=
(
select top 1 Count(*)
from #Something
group by P1_ID
order by COUNT(*) desc
)
declare #SqlToExecute nvarchar(max) = #StaticPortion + #DynamicPortion + #FinalStaticPortion;
exec sp_executesql #SqlToExecute

Columns to rows and rows to column conversion without pivot/unpivot

All I need is convert Column(A1/A2) into rows and rows(1 into Jan) into columns.
Input:
Here A1/A2 belongs to say A and they are calculated as A1/A2 for each month.
Month A1 A2 B1 B2 C1 C2
1 120 60 40 80 120 120
2 50 50 40 20 60 30
3 50 25 40 10 90 30
I need below o/p without using pivot and unpivot
O/P:
X Jan(1 is denoting Jan) Feb Mar
A 120/60(calculation:A1/A2) 40/80 120/120
B 50/50 40/20 60/30
C 50/25 40/10 90/30
I tried but my query is too long as I am using case and Union All three times each For A1 A2,B1 B2,C1 C2 etc.
Using cross apply(values ...) and conditional aggregation:
select
v.X
, Jan = max(case when t.month=1 then v.Value end)
, Feb = max(case when t.month=2 then v.Value end)
, Mar = max(case when t.month=3 then v.Value end)
from t
cross apply(values
('A',convert(varchar(3),A1)+'/'+convert(varchar(3),A2))
,('B',convert(varchar(3),B1)+'/'+convert(varchar(3),B2))
,('C',convert(varchar(3),C1)+'/'+convert(varchar(3),C2))
) v (X,Value)
group by v.X
rextester demo: http://rextester.com/XICI74484
returns:
+---+---------+-------+-------+
| X | Jan | Feb | Mar |
+---+---------+-------+-------+
| A | 120/60 | 50/50 | 50/25 |
| B | 40/80 | 40/20 | 40/10 |
| C | 120/120 | 60/30 | 90/30 |
+---+---------+-------+-------+
For the evaluation of the expressions:
select
v.X
, Jan = max(case when t.month=1 then v.Value end)
, Feb = max(case when t.month=2 then v.Value end)
, Mar = max(case when t.month=3 then v.Value end)
from t
cross apply(values
('A',(A1*1.0)/A2)
,('B',(B1*1.0)/B2)
,('C',(C1*1.0)/C2)
) v (X,Value)
group by v.X
returns:
+---+------+------+------+
| X | Jan | Feb | Mar |
+---+------+------+------+
| A | 2.00 | 1.00 | 2.00 |
| B | 0.50 | 2.00 | 4.00 |
| C | 1.00 | 2.00 | 3.00 |
+---+------+------+------+
You can use XML. This snippet may be more than you want but it shows how
--------------------------------------------------
-----------------Begin TRANSPOSE-----------------
--------------------------------------------------
DECLARE #xml XML
,#RowCount BIGINT
DECLARE #sSQl NVARCHAR(MAX)= 'SELECT (SELECT DISTINCT ColumnName FROM #TempTable WHERE CellId=Cell.CellId) as ColumnName,'
---------------------------------------------------
--Set up #table Here or outside the code... but #TABLE WILL BE DROPPED AT FINISH
---------------------------------------------------
--#############################################################################
-- the temp table #Table will contain what you want to Transpose
--#############################################################################
--SELECT * INTO #Table FROM Widgets W WHERE WidgetId IN (1096,803)
---------------------------------------------------
---------------- End #Table Setup ---------------
---------------------------------------------------
SET #xml = (SELECT *
,ROW_NUMBER() OVER (ORDER BY (SELECT 1
)) Rn
FROM #Table Row
FOR
XML AUTO
,ROOT('Root')
,ELEMENTS XSINIL
);
WITH RC AS
(SELECT COUNT(Row.value('.', 'nvarchar(MAX)')) [RowCount]
FROM #xml.nodes('Root/Row') AS WTable(Row))
,c AS(
SELECT b.value('local-name(.)','nvarchar(max)') ColumnName,
b.value('.[not(#xsi:nil = "true")]','nvarchar(max)') Value,
b.value('../Rn[1]','nvarchar(max)') Rn,
ROW_NUMBER() OVER (PARTITION BY b.value('../Rn[1]','nvarchar(max)') ORDER BY (SELECT 1)) Cell
FROM
#xml.nodes('//Root/Row/*[local-name(.)!="Rn"]') a(b)
),Cols AS (
SELECT DISTINCT c.ColumnName,
c.Cell
FROM c
)
INSERT INTO #TempTable (CellId,RowID,Value,ColumnName)
SELECT Cell,Rn,Value,REPLACE(c.ColumnName,'_x0023_','#')
FROM c
SELECT #sSQL = #sSQl + '(SELECT T2.Value FROM #Temptable T2 WHERE T2.CellId=Cell.CellID AND T2.Rowid=' + CAST(T.RowId AS NVARCHAR) + ') AS __________________Value____________________'
+ CAST(T.RowID AS NVARCHAR) + ','
FROM (SELECT DISTINCT
RowId
FROM #TempTable
) T
SET #sSQl = LEFT(#sSQL, LEN(#sSQL) - 1) + ' FROM (SELECT DISTINCT CellId FROM #TempTable) Cell order by columnname'
EXECUTE sp_Executesql #sSQl
--here you will have your output
-- PRINT #sSQl
DROP TABLE #Table
DROP TABLE #TempTable
--------------------------------------------------
-----------------END TRANSPOSE-------------------
--------------------------------------------------

CrossTab Query / Pivot Table in MS SQL?

I have a table that has the follow data structure:
terminal | load_time_mns | vehicle
_________________________________________
Terminal 1 | 3 | AA
Terminal 2 | 10 | AF
Terminal 1 | 1 | BF
Terminal 6 | 3 | QRS
Terminal 6 | 1.4 | AA
Terminal 3 | 2.5 | OP
I am trying to get an interval breakdown of load time from each terminal.For example, for the above table, I am trying to create a breakdown that looks like the following:
terminal | [0-1 mns] | [1-2 mns] | [2-3 mns] |
_______________________________________________________________
Terminal 1 | 0 | 1 | 1
_______________________________________________________________
Terminal 2 | 0 | 0 | 0
_______________________________________________________________
Terminal 3 | 0 | 0 | 1
_______________________________________________________________
Terminal 6 | 0 | 1 | 1
After a bit of Googling, it looks like I should be focusing on the pivot() function and crosstab queries. I am reading up on those two, but am still not quite able to get
Something like this might help:
Query 1:
SELECT
terminal,
count(CASE WHEN load_time_mns >= 0 AND load_time_mns < 1 THEN 1 END) [0-1 mns],
count(CASE WHEN load_time_mns >= 1 AND load_time_mns < 2 THEN 1 END) [1-2 mns],
count(CASE WHEN load_time_mns >= 2 AND load_time_mns < 3 THEN 1 END) [2-3 mns]
FROM t
GROUP BY terminal
Results:
| TERMINAL | 0-1 MNS | 1-2 MNS | 2-3 MNS |
|------------|---------|---------|---------|
| Terminal 1 | 0 | 1 | 0 |
| Terminal 2 | 0 | 0 | 0 |
| Terminal 3 | 0 | 0 | 1 |
| Terminal 6 | 0 | 1 | 0 |
Fiddle here.
Note that in your example you did not include 1 in the [0-1] range but you did include 3 in the [0-3] range, which seems not right.
You can use a bit of dynamic SQL to extend this to the complete result set. Create a table called intervals to store the intervals:
Create Table ex (
terminal varchar(10),
load_time_mns decimal(10, 2),
vehicle varchar(3)
);
Insert Into ex values
('Terminal 1', 3, 'AA'),
('Terminal 2', 10, 'AF'),
('Terminal 1', 1, 'BF'),
('Terminal 6', 3, 'QRS'),
('Terminal 6', 1.4, 'AA'),
('Terminal 3', 2.5, 'OP');
Create Table intervals (
min_mns decimal(10, 2),
max_mns decimal(10, 2),
column_name sysname
);
declare #i int = 0
while #i <= 20
begin
insert into intervals values (
#i, #i + 1, convert(varchar, #i) + '-' + convert(varchar, #i + 1)
);
set #i += 1;
end
while #i <= 420
begin
insert into intervals values (
#i, #i + 5, convert(varchar, #i) + '-' + convert(varchar, #i + 5)
);
set #i += 5;
end
You can then use a cursor to build up the complete SQL
declare
#sql nvarchar(max) = N'select terminal',
#lo int, #hi int, #col sysname;
declare pivot_cursor cursor local fast_forward for
select
min_mns, max_mns, column_name
from
intervals
order by
min_mns;
open pivot_cursor;
fetch next from pivot_cursor into #lo, #hi, #col;
while ##fetch_status = 0
begin
set #sql += ', sum(case when load_time_mns >= ' + convert(varchar, #lo)
+ ' and load_time_mns < ' + convert(varchar, #hi)
+ ' then 1 else 0 end) as [' + #col + ']';
fetch next from pivot_cursor into #lo, #hi, #col;
end
close pivot_cursor;
deallocate pivot_cursor;
Set #sql += ' from ex group by Terminal order by terminal';
exec sp_executesql #sql;
Example SQLFiddle
DECLARE #t TABLE ( terminal VARCHAR(10), load_time_mns DECIMAL(5,2), vehicle VARCHAR(3))
INSERT INTO #t ( terminal, load_time_mns, vehicle )
VALUES
('Terminal 1' , 3 , 'AA'),
('Terminal 2' , 10 , 'AF'),
('Terminal 2' , 20 , 'AF'),
('Terminal 1' , 1 , 'BF'),
('Terminal 1' , 25 , 'BF'),
('Terminal 6' , 3 , 'QRS'),
('Terminal 6' , 1.4 , 'AA'),
('Terminal 3' , 2.5 , 'OP')
;WITH intervals AS
(
SELECT m = 0, n = 1
UNION ALL
SELECT CASE WHEN m < 20 THEN m+1 ELSE m+5 END, CASE WHEN n < 20 THEN n+1 ELSE n+5 END
FROM intervals
WHERE n<420
)
SELECT terminal, load_time_mns, vehicle,
interval = CAST(m AS VARCHAR(10)) + '-' + CAST(n AS VARCHAR(10)), m
INTO ##tmp
FROM intervals i
LEFT JOIN #t t
ON t.load_time_mns >= i.m
AND t.load_time_mns < i.n
OPTION (MAXRECURSION 0)
DECLARE #cols VARCHAR(MAX) =
STUFF(CAST((SELECT ',' + QUOTENAME(interval)
FROM (
SELECT DISTINCT interval, m
FROM ##tmp
) t
ORDER BY m
FOR XML PATH(''), TYPE
) AS VARCHAR(MAX)),1,1,'')
DECLARE #sql VARCHAR(MAX) = '
SELECT terminal, ' + #cols + '
FROM (
SELECT terminal, vehicle, interval
FROM ##tmp
) t
PIVOT (
COUNT(vehicle)
FOR interval IN (' + #cols + ')
) p
'
EXEC(#sql)
DROP TABLE ##tmp

Update each row of one table with multiple row data from other table using join condition in sql server 2012

DECLARE #outerCounter INT = 1, #rowCount INT,
#query NVARCHAR(MAX), #load numeric(18,3), #batcolno NVARCHAR(MAX), #inCounter INT = 1,#innerrowsCount INT
SELECT #rowCount = COUNT(*) FROM tempA
WHILE(#outercounter <= #rowCount)
BEGIN
SELECT #innerrowsCount = COUNT(*)
FROM
tempB INNER JOIN tempA
ON tempB.batteryid = tempA.batteryid
AND
tempB.uniquerowid = #outercounter
WHILE(#inCounter <= #innnerrowCounter)
BEGIN
SELECT #laod = laod FROM tempB INNER JOIN tempA
ON tempB.batteryid = tempA.batteryid
AND
tempB.uniquerowid = #inCounter
SET #testcolno = 'batt' + REPLACE(STR(#inCounter,3),' ','0')
SET #query = ' UPDATE tempA
SET '+ #testcolno + ' = '+ #load +'
WHERE tempA.rowid = '+ #outercounter + ' '
EXEC(#query)
SET #inCounter = #inCounter + 1
END
SET #outercounter = #outercounter + 1
END
I have 2 tables tempA and tempB.
tempA:
batteryid | batname | batt01 | batt02 | batt03 | batt04
----------+---------+---------+--------+---------+--------
01 | trixon | null | null | null | null
03 | jaguarv | null | null | null | null
tempB:
batteryid | load
-----------+---------
01 | 14.58
01 | 58.12
01 | 16.89
03 | 25.47
03 | 87.65
Final output in tempA should be like this:
batteryid | batname | batt01 | batt02 | batt03 | batt04
----------|----------|----------|--------|---------|--------------
01 | trixon | 14.58 | 58.12 | 16.89 | null
03 | jaguarv | 25.47 | 87.65 | null | null
Above code uses while loop to update the tempA table by joining batteryid with that of tempB table.
Thanks
You can get away with using a PIVOT in SQL Server, and you'll be able to do away with the loop and update statements.
SELECT SourceA.[batteryid],
SourceA.[batname],
SourcePivot.[1] AS 'Battery1',
SourcePivot.[2] AS 'Battery2',
SourcePivot.[3] AS 'Battery3',
SourcePivot.[4] AS 'Battery4'
FROM #tempA AS SourceA
INNER JOIN (
SELECT *
FROM
(
SELECT batteryId,
loadval,
row_number() OVER (PARTITION BY batteryid ORDER BY loadval) rn
FROM #tempB
) s
PIVOT (MIN(loadval) FOR rn IN ([1], [2], [3], [4])) pvt
) AS SourcePivot ON SourcePivot.batteryid = SourceA.batteryid
Here is the code that I used to test the query
DECLARE #tempA TABLE (
batteryid VARCHAR(2),
batname VARCHAR(50),
batt01 DECIMAL(18, 2),
batt02 DECIMAL(18, 2),
batt03 DECIMAL(18, 2),
batt04 DECIMAL(18, 2)
)
DECLARE #tempB TABLE (
batteryid VARCHAR(2),
loadval DECIMAL(18, 2)
)
INSERT INTO #tempA (batteryid, batname) VALUES ('01', 'trixon')
INSERT INTO #tempA (batteryid, batname) VALUES ('03', 'jaguarv')
INSERT INTO #tempB VALUES ('01', '14.58')
INSERT INTO #tempB VALUES ('01', '58.12')
INSERT INTO #tempB VALUES ('01', '16.89')
INSERT INTO #tempB VALUES ('03', '25.47')
INSERT INTO #tempB VALUES ('03', '87.65')