SQL max of multiple columns in pivot table - sql-server-2008

How can you find the max of several columns that are created in a pivot table using a SQL Server 2008 pivot table query?
Given:
create table ElectionResults_test
(
Total_Votes int,
Precinct_Name varchar(50),
Candidate_Name varchar(50)
)
insert into ElectionResults_test values (4,'CP01', 'DOUG')
insert into ElectionResults_test values (2,'CP02', 'DOUG')
insert into ElectionResults_test values (2,'CP01', 'LATHE')
insert into ElectionResults_test values (4,'CP02', 'LATHE')
SELECT Precinct_Name as ConsPrecinct_Name, 'DOUG' AS Candidate1, [DOUG] AS NumVotes1,
'LATHE' AS Candidate2, [LATHE] AS NumVotes2, 'Needs Data' as WinningCandidate FROM
(Select Total_Votes, Precinct_Name, Candidate_Name from [ELECTIONRESULTS_test])
as SourceTable pivot (sum(Total_Votes) for Candidate_Name in ([DOUG], [LATHE])) as PivotTable
The select statement above has the following output:
ConsPrecinct_name Candidate1 NumVotes1 Candidate2 NumVotes2 Winning Candidate
CP01 DOUG 4 LATH 2 Needs Data
CP01 DOUG 2 LATH 4 Needs Data
The goal is to have the 'Winning Candidate' field populated with the candidate name that has the most votes in the corresponding NumVotes field.

To deal with 8 way contests more easily you can use CROSS APPLY and VALUES, you may also want a GROUP BY as you haven't said how ties will be handled (this will return multiple rows for each winner)
SELECT Precinct_Name AS ConsPrecinct_Name,
'DOUG' AS Candidate1,
[DOUG] AS NumVotes1,
'LATHE' AS Candidate2,
[LATHE] AS NumVotes2,
WinningCandidate.name AS WinningCandidate
FROM (SELECT Total_Votes,
Precinct_Name,
Candidate_Name
FROM ElectionResults_test) AS SourceTable PIVOT (SUM(Total_Votes) FOR
Candidate_Name IN ([DOUG], [LATHE])) AS PivotTable
CROSS APPLY (SELECT CASE
WHEN COUNT(*) = 1 THEN MAX(name)
ELSE 'Tie'
END AS name
FROM (SELECT TOP 1 WITH TIES name
FROM (VALUES('DOUG', [DOUG]),
('LATHE', [LATHE])) Y(name, votes)
ORDER BY votes DESC) T)AS WinningCandidate

Try a CASE statement:
CASE WHEN [DOUG] > [LATHE] THEN 'DOUG'
WHEN [DOUG] < [LATHE] THEN 'LATHE'
ELSE 'No winner'
END AS WinningCandidate

If it's only a few fields you can use a CASE statement:
...
CASE WHEN NumVotes1 > NumVotes2 THEN Candidate1
WHEN NumVotes2 > NumVotes1 THEN Candidate2
ELSE 'TIE' END as WinningCandidate

Related

how to use concat, like and subconsultas with where?

SELECT asset_tag.asset_id, LEFT(asset_tag,SUBSTRING(asset_tag)-1 AS 'ETIQ'
from (SELECT DISTINCT S2.asset_id + ',' AS etiquetas
(SELECT S1.tag_id
FROM asset_tag AS S1
WHERE S1.tag_id
ORDER BY S1.tag_id
FOR XML PATH (''),TYPE
).VALUE('TEXT(1)`[1]','ninteger(MAX')[aset_tag] FROM asset_tag AS S2 ) asset_tag;
I have to group by asset and the asset 1 have in one column 1,2,3,4,5 or the tag that it have
how to use heidisql functions, on dbforge? I know but here not I use heidisql version 12. and is my first time working with this
The objective is that the source table that has two columns, group by column 1 and that a new column indicate separated by commas what column 1 has in column 2 (of origin).
columna 1 - 1 1 2 2 3 3 4 4
columna 2 - a b c a d a f g
and in a new column or table 1 - a b / 2 - b c
I see this answer on this page: https://stackoverflow.com/a/545672/20100117 But i don´t know what mean "st1" or [text()] the alias?
SELECT Main.SubjectID,
LEFT(Main.Students,Len(Main.Students)-1) As "Students" FROM
(
SELECT DISTINCT ST2.SubjectID,
(
SELECT ST1.StudentName + ',' AS [text()]
FROM dbo.Students ST1
WHERE ST1.SubjectID = ST2.SubjectID
ORDER BY ST1.SubjectID
FOR XML PATH (''), TYPE
).value('text()[1]','nvarchar(max)') [Students]
FROM dbo.Students ST2
) [Main]
If you use HeidiSQL, you can use the completion proposal to help finding the right syntax for the various functions. Just type some first characters and press Ctrl+Space:
Here's a basic example of how SUBSTRING() works:
SELECT SUBSTRING(name, 2, 3) FROM mytable;
How to concatenate rows of the same column in PostgreSQL?
Given that you want to concatenate rows of the same column, and not different columns (which is what you do when using CONCAT_WS()), what you would really be looking for is to use the ARRAY_AGG aggregation function within the ARRAY_TO_STRING function.
Documentation: https://www.postgresql.org/docs/13/functions-array.html
solution:
SELECT
a.asset_id, ARRAY_TO_STRING(ARRAY_AGG(a.tag_id), ',') AS etiqueta
FROM public.asset_tag AS a
GROUP BY a.asset_id;
Result:
on asset_id 1 | 1,3,5 tag_id
on asset_id 6 | 1,2 tag_id
If you insert this:
CREATE TABLE asset_tag ( asset_id INT,tag_id INT);
INSERT INTO asset_tag VALUES (1,1);
INSERT INTO asset_tag VALUES (1,3);
INSERT INTO asset_tag VALUES (1,5);
INSERT INTO asset_tag VALUES (6,1);
INSERT INTO asset_tag VALUES (6,2);
thanks to the person who gave me this answer .

Is there a way to use aggregate COUNT() values within CASE?

I need to retrieve unique yet truncated part numbers, with their description values being conditionally determined.
DATA:
Here's some simplified sample data:
(the real table has half a million rows)
create table inventory(
partnumber VARCHAR(10),
description VARCHAR(10)
);
INSERT INTO inventory (partnumber,description) VALUES
('12345','ABCDE'),
('123456','ABCDEF'),
('1234567','ABCDEFG'),
('98765','ZYXWV'),
('987654','ZYXWVU'),
('9876543','ZYXWVUT'),
('abcde',''),
('abcdef','123'),
('abcdefg','321'),
('zyxwv',NULL),
('zyxwvu','987'),
('zyxwvut','789');
TRIED:
I've tried too many things to list here.
I've finally found a way to get past all the 'unknown field' errors and at least get SOME results, but:
it's SUPER kludgy!
my results are not limited to unique prods.
Here's my current query:
SELECT
LEFT(i.partnumber, 6) AS prod,
CASE
WHEN agg.cnt > 1
OR i.description IS NULL
OR i.description = ''
THEN LEFT(i.partnumber, 6)
ELSE i.description
END AS `descrip`
FROM inventory i
INNER JOIN (SELECT LEFT(ii.partnumber, 6) t, COUNT(*) cnt
FROM inventory ii GROUP BY ii.partnumber) AS agg
ON LEFT(i.partnumber, 6) = agg.t;
GOAL:
My goal is to retrieve:
prod
descrip
12345
ABCDE
123456
123456
98765
ZYXWV
987654
987654
abcde
abcde
abcdef
abcdef
zyxwv
zyxwv
zyxwvu
zyxwvu
QUESTION:
What are some cleaner ways to use the COUNT() aggregate data with a CASE type conditional?
How can I limit my results so that all prods are UNIQUE?
You can check if a left(partnumber, 6) is not unique in the result by checking if count(*) > 1. In such a case let descrip be left(partnumber, 6). Otherwise you can use max(description) (or min(description)) to get the single description but satisfy the needs to use an aggregation function on columns not in the GROUP BY. To replace empty or NULL descriptions, nullif() and coalesce() can be used.
That would lead to the following using just one level of aggregation and no joins:
SELECT left(partnumber, 6) AS prod,
CASE
WHEN count(*) > 1 THEN
left(partnumber, 6)
ELSE
coalesce(nullif(max(description), ''), left(partnumber, 6))
END AS descrip
FROM inventory
GROUP BY left(partnumber, 6)
ORDER BY left(partnumber, 6);
But there seems to be a bug in MySQL and this query fails. The engine doesn't "see" that, in the list after SELECT partnumber is only used in the expression left(partnumber, 6), which is also in the GROUP BY. Instead the engine falsely complains about partnumber not being in the GROUP BY and not subject to an aggregation function.
As a workaround, we can use a derived table, that does the shortening of partnumber to its first six characters. We then use use that column of the derived table instead of left(partnumber, 6).
SELECT l6pn AS prod,
CASE
WHEN count(*) > 1 THEN
l6pn
ELSE
coalesce(nullif(max(description), ''), l6pn)
END AS descrip
FROM (SELECT left(partnumber, 6) AS l6pn,
description
FROM inventory) AS x
GROUP BY l6pn
ORDER BY l6pn;
Or we slap some actually pointless max()es around the left(partnumber, 6) other than the first, to work around the bug.
SELECT left(partnumber, 6) AS prod,
CASE
WHEN count(*) > 1 THEN
max(left(partnumber, 6))
ELSE
coalesce(nullif(max(description), ''), max(left(partnumber, 6)))
END AS descrip
FROM inventory
GROUP BY left(partnumber, 6)
ORDER BY left(partnumber, 6);
db<>fiddle (Change the DBMS to some other like Postgres or MariaDB to see that they also accept the first query.)

convert all JSON columns into new table

I currently have a table structured like:
customer_id name phoneNumbers
1 Adam [{'type':'home','number':'687-5309'} , {'type':'cell','number':'123-4567'}]
2 Bill [{'type':'home','number':'987-6543'}]
With the phoneNumbers column set as a JSON column type.
For simplicity sake though I am wanting to covert all the JSON phone numbers into a new separate table.
Something like:
phone_id customer_id type number
1 1 home 687-5309
2 1 cell 123-4567
3 2 home 987-6543
It seems like it should be do-able with OPENJSON but so far I haven't had any luck in figuring out how to declare it correctly. Any help is appreciated.
USE recursive CTE with 1 and recurse upto json_length.
SELECT c.*, JSON_LENGTH(c.phoneNumbers) as json_length
from customers c;
then use concat to pass that element_id in Extract Query:
(json_unquote(JSON_EXTRACT(phoneNumbers, CONCAT('$.type.',1))), json_unquote(JSON_EXTRACT(phoneNumbers, CONCAT('$.number.',1))))
(json_unquote(JSON_EXTRACT(phoneNumbers, CONCAT('$.type.',2))), json_unquote(JSON_EXTRACT(phoneNumbers, CONCAT('$.number.',1))))
-
-
-
(json_unquote(JSON_EXTRACT(phoneNumbers, CONCAT('$.type.',json_length))), json_unquote(JSON_EXTRACT(phoneNumbers, CONCAT('$.number.',json_length))))
You can do something like this:
SELECT id,
name,
JSON_UNQUOTE(JSON_EXTRACT(phone, CONCAT("$[", seq.i, "]", ".", "number"))) AS NUMBER,
JSON_UNQUOTE(JSON_EXTRACT(phone, CONCAT("$[", seq.i, "]", ".", "type"))) AS TYPE
FROM customer, (SELECT 0 AS I UNION ALL SELECT 1) AS seq
WHERE seq.i < json_length(phone)
The trick is (SELECT 0 as i union all SELECT 1), depends on your JSON array's length you may need to add more index. You can find out the max length by:
SELECT MAX(JSON_LENGTH(phone)) FROM customer;
Please change CTE defination syntax according to MySQL\Maria versions.
WITH RECURSIVE cte_recurse_json AS
(
SELECT customer_id, phone_numbers, 0 as recurse, JSON_LENGTH(c.phoneNumbers) as json_length,
json_unquote(JSON_EXTRACT(phoneNumbers, CONCAT('$[',0,'].type'))) as type,
json_unquote(JSON_EXTRACT(phoneNumbers, CONCAT('$[',0,'].number'))) as number
FROM table
UNION ALL
SELECT t.customer_id, t.phone_numbers, ct.recurse + 1 as recurse, t.json_length,
json_unquote(JSON_EXTRACT(ct.phoneNumbers, CONCAT('$[',ct.recurse,'].type'))) as type,
json_unquote(JSON_EXTRACT(ct.phoneNumbers, CONCAT('$[',ct.recurse,'].number'))) as number
FROM TABLE t
INNER JOIN cte_recurse_json ct ON t.customer_id = ct.customer_id
WHERE ct.recurse < json_length
)
SELECT customer_id, type, number FROM cte_recurse_json;

sql server rank using keywords

I have a table which contains Bank ID (not unique) and Bank names in a table. If the bank name contains keyword 'bank' I want to rank it one and if it
has 'financial' rank it two and 'Trust' gets a rank of three.
Create table dbo.banknames(id int null,bankname varchar(200) null)
insert into dbo.bankname(1,'U.S. Trust')
insert into dbo.bankname(1,'Bank of america')
insert into dbo.bankname(1,'BOA Financial services')
insert into dbo.bankname(2,'Citizens trust')
insert into dbo.bankname(3,'People''s bank trust')
insert into dbo.bankname(3,'People''s financial service')
The output should look like
Create table dbo.ExpectedOUTPUTbanknames(id int null,bankname varchar(200) null,rank int null)
insert into dbo.ExpectedOUTPUTbankname(1,'U.S. Trust',3)
insert into dbo.ExpectedOUTPUTbankname(1,'Bank of america',1)
insert into dbo.ExpectedOUTPUTbankname(1,'BOA Financial services',2)
insert into dbo.ExpectedOUTPUTbankname(2,'Citizens trust',3)
insert into dbo.ExpectedOUTPUTbankname(3,'People''s bank trust',1)
insert into dbo.ExpectedOUTPUTbankname(3,'People''s financial service',2)
select *
into dbo.OUTPUTbankname
(
SELECT *,1 as RNK FROM dbo.banknames
WHERE (bankname LIKE '%bank%')
UNION
SELECT *,1 as RNK FROM dbo.banknames
WHERE (bankname LIKE '%financial%')
UNION
SELECT *,1 as RNK FROM dbo.banknames
WHERE (bankname LIKE '%trust%')
) qrey
For bankid =3, since bankname has both keywords bank and trust, I get 3 rows, 'People''s bank trust' with rank=1 'People''s financial service'with rank=2
'People''s bank trust' with rank=3. How can i avoid this
Thanks
MR
Use a CASE expression:
SELECT id,
bankname,
CASE WHEN bankname LIKE '%[Bb]ank%' THEN 1
WHEN bankname LIKE '%[Ff]inancial%' THEN 2
WHEN bankname LIKE '%[Tt]rust%' THEN 3
END AS rank
FROM dbo.banknames
Fortunately SQL Server supports character ranges in its LIKE expressions, making it easy to match the keywords regardless of whether the first letter be capitalized or not.

Adding First Values Grouped by ID from record set; report builder 3.0

I have a dataset being returned that has monthly values for different 'Goals.' The goals has unique ID's and the month/date values will always be the same for the goals. The difference is sometimes one goal doesn't have values for all the same months as the other goal because it might start at a later date, and i want to 'consolidate' the results and sum them together based on the 'First' startBalance for each goal. Example dataset would be;
goalID monthDate startBalance
1 1/1/2014 10
1 2/1/2014 15
1 3/1/2014 22
1 4/1/2014 30
2 4/1/2014 13
2 5/1/2014 29
What i want to do is display these consolidated (summed) values in a table based on the 'First' (earliest Month/Year) value for each goal. The result would look like;
Year startBalance
2014 23
This is because the 'First' value for goalID of 1 is 10 and the 'First' value for goalID of 2 is '13' but when I try to group by the
Year(Fields!MonthDate.Value)
and use the expression;
Sum(First(Fields!startBalance.Value))
I receive the error;
The Value expression for the textrun ‘StartingValue3.Paragraphs[0].TextRuns[0]’ uses a First, Last or Previous aggregate in an outer aggregate. These aggregate functions cannot be specified as nested aggregates.
Does anyone know if my grouping is incorrect, or if there's a different way i can get the 'First' value for the goalIDs summed together correctly?
You have to change
Sum(First(Fields!startBalance.Value))
to
Sum(Fields!startBalance.Value)
This is Code that you exactly want:
Copy
create table #temp
(id int,
monthDate date,
value int)
insert into #temp values(1,'1/1/2014',10)
insert into #temp values(1,'1/2/2014',15)
insert into #temp values(1,'1/3/2014',20)
insert into #temp values(2,'1/4/2014',25)
insert into #temp values(2,'1/5/2014',19)
declare #min int,#max int
select #min=MIN(ID) from #temp
select #max=MAX(ID) from #temp
select * from #temp --This is your main table
select top 0 * into #res
from #temp
while(#min<=#max)
begin
declare #minDT date
set #minDT=(select MIN(MonthDate) from #temp where id=#min)
insert into #res
select *
from #temp
where ID=#min
and Convert(Date,monthDate,103)=Convert(Date,#minDT,103)
set #min=#min+1
end
select * from #res --This is Result
drop table #res
drop table #temp