Here is a sample code:
SELECT DISTINCT salary, planet,
sum(case when company LIKE '%Google%' then 1 end) `Google`,
sum(case when company LIKE '%IBM%' then 1 end) `IBM`,
sum(case when company LIKE '%Cisco%' then 1 end) `Cisco`
from industries
where planet = 'Earth' ;
Can someone give me advice how to summarize amount of multiple variables defined outside case condition? I tried to use simple math, but it did not work.
SELECT DISTINCT salary, planet,
sum(case when company LIKE '%Google%' then 1 end) `Google`,
sum(case when company LIKE '%IBM%' then 1 end) `IBM`,
sum(case when company LIKE '%Cisco%' then 1 end) `Cisco`,
-- similar math to count multiple columns,
sum(`Google` + `IBM` + `Cisco`) AS Total_amount
from industries
where planet = 'Earth' ;
The result should like this:
------------------------------------------------------------
| salary | Planet| Google | IBM | Cisco | Total_amount |
|----------------------------------------------------------|
| 3000.00 | Earth | 26 | 26 | 25 | 77 |
------------------------------------------------------------
Thanks in advance!
It's just in front of your eyes. It happens to me, too. Just COUNT(*) for the total amount.
WITH industries(salary,planet,company) AS (
SELECT 3000.00,'Earth','Google'
UNION ALL SELECT 3000.00,'Earth','Google'
UNION ALL SELECT 3000.00,'Earth','Google'
UNION ALL SELECT 3000.00,'Earth','Google'
UNION ALL SELECT 3000.00,'Earth','IBM'
UNION ALL SELECT 3000.00,'Earth','IBM'
UNION ALL SELECT 3000.00,'Earth','IBM'
UNION ALL SELECT 3000.00,'Earth','Cisco'
UNION ALL SELECT 3000.00,'Earth','Cisco'
UNION ALL SELECT 3000.00,'Earth','Cisco'
UNION ALL SELECT 3000.00,'Earth','Cisco'
UNION ALL SELECT 3000.00,'Earth','Cisco'
UNION ALL SELECT 3000.00,'Earth','Cisco'
)
SELECT
salary
, planet
, SUM(CASE company WHEN 'Google' THEN 1 END) AS Google
, SUM(CASE company WHEN 'IBM' THEN 1 END) AS IBM
, SUM(CASE company WHEN 'Cisco' THEN 1 END) AS Cisco
, COUNT(*) AS total_amount
FROM industries
WHERE planet = 'Earth'
GROUP BY
salary
, planet
;
-- out salary | planet | Google | IBM | Cisco | total_amount
-- out ---------+--------+--------+-----+-------+--------------
-- out 3000.00 | Earth | 4 | 3 | 6 | 13
Consider below (BigQuery)
SELECT salary, planet,
COUNTIF(company LIKE '%Google%') AS Google,
COUNTIF(company LIKE '%IBM%') AS IBM ,
COUNTIF(company LIKE '%Cisco%') AS Cisco,
COUNTIF(REGEXP_CONTAINS(company, 'Google|IBM|Cisco')) AS Total_amount
FROM industries
WHERE planet = 'Earth'
GROUP BY salary, planet
Yet another approach (BigQuery)
SELECT *, Google + IBM + Cisco AS Total_amount
FROM (
SELECT * EXCEPT(company),
REGEXP_EXTRACT(company, 'Google|IBM|Cisco') as col
FROM industries
WHERE planet = 'Earth'
)
PIVOT (COUNT(*) FOR col IN ('Google','IBM','Cisco'))
Related
company | email | phone | website | address
Amar CO LLC | amar#gmail.com | 123 | NULL | India
Amar CO | amar#gmail.com | NULL | NULL | IND
Stacks CO | stack#gmail.com | 910 | stacks.com | United Kingdom
Stacks CO LLC | stack#gmail.com | NULL | NULL | UK
I want to drop the company name with CO LLC instead want to keep Amar CO but want all the columns from Amar CO LLC as it has minimum NULL values or maximum column data.
In short: De-dupe the records, remove the company name with 'ending with or matching with LLC' (case insensitive), but keep the values from both of the record which has maximum Information column.
Expected output
Amar CO | amar#gmail.com | 123 | NULL | India
Stacks CO | stack#gmail.com | 910 | stacks.com | United Kingdom
to give precedence to the record having minimum null values ...
Below is for BigQuery Standard SQL (query#1)
#standardSQL
select
array_agg(t
order by array_length(regexp_extract_all(to_json_string(t), ':null'))
limit 1
)[offset(0)].*
replace(regexp_replace(company, r'(?i)CO LLC', 'CO') as company)
from `project.dataset.table` t
group by company
if applied to sample data from your question - output is
In case if you want to fill all fields from all the records - you can use below (query#2)
select regexp_replace(company, r'(?i)CO LLC', 'CO') as company,
max(email) email,
max(phone) phone,
max(website) website,
max(address) address
from `project.dataset.table`
group by company
and finally - if you still want to give precedence to the record having minimum null values, but the rest of nulls replace with values from other rows - use below (query#3)
select company,
ifnull(email, max_email) email,
ifnull(phone, max_phone) phone,
ifnull(website, max_website) website,
ifnull(address, max_address) address
from (
select array_agg(t
order by array_length(regexp_extract_all(to_json_string(t), ':null'))
limit 1
)[offset(0)].*
replace(regexp_replace(company, r'(?i)CO LLC', 'CO') as company),
max(email) max_email,
max(phone) max_phone,
max(website) max_website,
max(address) max_address
from `project.dataset.table` t
group by company
)
you can test/check the difference between this and previous option by applying them to below dummy data
with `project.dataset.table` as (
select 'Amar CO LLC' company, 'amar#gmail.com' email, 123 phone, NULL website, 'India' address union all
select 'Amar CO', NULL, 222, 'amar.com', NULL union all
select 'Stacks CO LLC', 'stack#gmail.com', NULL, NULL, 'UK' union all
select 'Stacks CO', 'stack#gmil.com', 910, 'stacks.com', 'United Kingdom'
)
the last query (query#3) gives
while previous (query#2) will just give max across all rows
You need group by and replace as follows:
select replace(company,' LLC','') as company, max(email) as email, max(phone) as phone,
max(website) as website, max(address) as address
from your_table t
group by replace(company,' LLC','')
I can see that you need all data of the both rows but precedence should be given to LLC record (India, IND --> India) then you can use it as follows:
select t.company,
coalesce(tt.email,t.emial) as email,
coalesce(tt.phone,t.phone) as phone
coalesce(tt.website,t.website) as website,
coalesce(tt.address,t.address) as address
from your_table t join your_table tt
on concat(t.company,' LLC') = tt.company
If you want to update the data and then drop the record itself, I would suggest the following delete and update.
delete from your_table where t.company = 'Amar CO';
update your_table t
set t.comapny = replace(company,' LLC','') -- or use 'Amar CO'
where t.company = 'Amar CO LLC';
-- Update
You want to give precedence to the record having minimum null values then you can use the following query:
select t.company,
case when tt_nulls > t_nulls then ttemail else temail end as email,
case when tt_nulls > t_nulls then ttphone else tphone end as phone,
case when tt_nulls > t_nulls then ttwebsite else twebsite end as website,
case when tt_nulls > t_nulls then taddress else taddress end as address
from
(select t.company,
count(case when t.email IS NULL THEN 1 end) over (partition by t.company)
+ count(case when t.phone IS NULL THEN 1 end) over (partition by t.company)
+ count(case when t.website IS NULL THEN 1 end) over (partition by t.company)
+ count(case when t.address IS NULL THEN 1 end) over (partition by t.company)
as t_nulls,
count(case when tt.email IS NULL THEN 1 end) over (partition by t.company)
+ count(case when tt.phone IS NULL THEN 1 end) over (partition by t.company)
+ count(case when tt.website IS NULL THEN 1 end) over (partition by t.company)
+ count(case when tt.address IS NULL THEN 1 end) over (partition by t.company)
as tt_nulls
t.email as temail,
t.phone as tphone,
t.website as twebsite,
t.address as taddress,
tt.email as ttemail,
tt.phone as ttphone,
tt.website as ttwebsite,
tt.address as ttaddress
from your_table t join your_table tt
on concat(t.company,' LLC') = tt.company) t
I am using DB Browser for SQLite. I have a table in the following format:
+-----------+-------------------------------------+
| search_id | search_town |
+-----------+-------------------------------------+
| 1 | town1,town3 |
| 2 | town2,town4,town5 |
| 3 | town3,town5 |
| 4 | town2,town5 |
| 5 | town2,town3,town4 |
+-----------+-------------------------------------+
I would like to do a COUNT on the number of times town1 through town5 has appeared under search_town, and then rank in descending order the towns based on their respective counts. So far I have the following query:
SELECT SUM(CASE WHEN search_location LIKE '%town01%' THEN 1 ELSE 0 END) AS town01,
SUM(CASE WHEN search_location LIKE '%town02%' THEN 1 ELSE 0 END) AS town02,
SUM(CASE WHEN search_location LIKE '%town03%' THEN 1 ELSE 0 END) AS town03,
SUM(CASE WHEN search_location LIKE '%town04%' THEN 1 ELSE 0 END) AS town04,
SUM(CASE WHEN search_location LIKE '%town05%' THEN 1 ELSE 0 END) AS town05
FROM searches
...but am unable to do an ORDER BY as the towns and their counts are output as columns instead of rows in this format
+-------+-------+-------+-------+-------+
| town1 | town2 | town3 | town4 | town5 |
+-------+-------+-------+-------+-------+
| 12 | 31 | 12 | 24 | 12 |
+-------+-------+-------+-------+-------+
Is there another approach to this? Appreciate any comments.
You are turning your output in a single row using CASE WHEN, to convert it into multiple rows, you can try like following.
;WITH cte
AS (SELECT *
FROM (VALUES ('Town1'),
('Town2'),
('Town3'),
('Town4'),
('Town5')) T(town))
SELECT Count(*) [Count],
C.town
FROM [TABLE_NAME] T
INNER JOIN cte C
ON T.search_location LIKE '%' + C.town + '%'
GROUP BY C.town
ORDER BY Count(*) DESC
Online DEMO
Another approach can be using UNION ALL.
SELECT *
FROM (SELECT Count(*) s,
'Town1' AS Col
FROM tablename
WHERE search_location LIKE '%town1%'
UNION ALL
SELECT Count(*) s,
'Town2' AS Col
FROM tablename
WHERE search_location LIKE '%town2%'
UNION ALL
SELECT Count(*) s,
'Town3' AS Col
FROM tablename
WHERE search_location LIKE '%town3%'
UNION ALL
SELECT Count(*) s,
'Town4' AS Col
FROM tablename
WHERE search_location LIKE '%town4%'
UNION ALL
SELECT Count(*) s,
'Town5' AS Col
FROM tablename
WHERE search_location LIKE '%town5%') t
ORDER BY s DESC
You can use a recursive common-table expression (CTE) to turn the comma-separated list into a set of rows. When the table is normalized, you can group by town and sort by descending count:
WITH rec(town, remain)
AS (
SELECT SUBSTR(search_town, 0, INSTR(search_town, ',')) -- Before ,
, SUBSTR(search_town, INSTR(search_town, ',')+1) || ',' -- After ,
FROM t1
UNION ALL
SELECT SUBSTR(remain, 0, INSTR(remain, ',')) -- Before ,
, SUBSTR(remain, INSTR(remain, ',')+1) -- After ,
FROM rec
WHERE LENGTH(remain) > 0
)
SELECT town
, COUNT(*)
FROM rec
GROUP BY
town
ORDER BY
COUNT(*) DESC
Idea from this blog post. Working example at sqliteonline.
There is my current query:
SELECT Name, Code, Today
, Account || Currency as Accounts
FROM (
SELECT
b.description AS Name
, b.contragentidentifycode AS Code
, c.systemday AS Today
, b.accountno AS Account
, b.currencysname AS Currency
FROM vAACCOUNT b, currentdaysetting c
WHERE b.contragentid = 412
AND b.accountno LIKE '26%'
)
it gives me such result:
Name | Code | Today | Accounts
---------------------------------------
name1 | code1 | 07.09.2016 | acc1+curr1
name1 | code1 | 07.09.2016 | acc2+curr1
name1 | code1 | 07.09.2016 | acc1+curr2
name1 | code1 | 07.09.2016 | acc2+curr2
name1 | code1 | 07.09.2016 | acc1+curr3
name1 | code1 | 07.09.2016 | acc2+curr3
name1 | code1 | 07.09.2016 | acc1+curr4
name1 | code1 | 07.09.2016 | acc2+curr4
I need convert this view to:
Name | Code | Today | someName1 | someName2 | someName3 | someName4 | someName5 | someName6 | someName7 | someName8
-------------------------------------------------------------------------------------------------------------------------------------------
name1 | code1 | 07.09.2016 | acc1+curr1 | acc2+curr1 | acc1+curr2 | acc2+curr2 | acc1+curr3 | acc2+curr3 | acc1+curr4 | acc2+curr4
I guess that most probably for this I have to use the keyword "Pivot". But all my attempts to do so - have failed. I can not to project what I see in the examples, to my table. Please help.
For number of columns I can add such "id" column:
SELECT id, Name, Code, Today
, Account || Currency as Accounts
FROM (
SELECT
row_number() over (ORDER BY b.id) AS id
, b.description AS Name
...
In my scenario:
numbers of accounts may be different;
name, code and data - one per query;
combination of accaunt+currency are unique;
result should be in one line;
total number of lines in result of query, cannot be more then 10 (in my example 8)
Per my comment above, I don't think PIVOT works for you. The answer from #RoundFour works, but requires that you know, and code for, all possible values for Account || Currency. This suggests there will never be new values for these items - I find that unlikely.
The following will allow you to switch the shape of your data. It makes no assumptions about the values in your data, but it does assume a limit on the number of possible combinations - I have coded for eight.
WITH account_data (name,code,today,account)
AS
(
SELECT 'name1','code1',TO_DATE('07.09.2016','DD.MM.YYYY'),'acc1+curr1' FROM dual UNION ALL
SELECT 'name1','code1',TO_DATE('07.09.2016','DD.MM.YYYY'),'acc2+curr1' FROM dual UNION ALL
SELECT 'name1','code1',TO_DATE('07.09.2016','DD.MM.YYYY'),'acc1+curr2' FROM dual UNION ALL
SELECT 'name1','code1',TO_DATE('07.09.2016','DD.MM.YYYY'),'acc2+curr2' FROM dual UNION ALL
SELECT 'name1','code1',TO_DATE('07.09.2016','DD.MM.YYYY'),'acc1+curr3' FROM dual UNION ALL
SELECT 'name1','code1',TO_DATE('07.09.2016','DD.MM.YYYY'),'acc2+curr3' FROM dual UNION ALL
SELECT 'name1','code1',TO_DATE('07.09.2016','DD.MM.YYYY'),'acc1+curr4' FROM dual UNION ALL
SELECT 'name1','code1',TO_DATE('07.09.2016','DD.MM.YYYY'),'acc2+curr4' FROM dual UNION ALL
SELECT 'name2','code1',TO_DATE('07.09.2016','DD.MM.YYYY'),'acc1+curr1' FROM dual UNION ALL
SELECT 'name2','code1',TO_DATE('07.09.2016','DD.MM.YYYY'),'acc2+curr1' FROM dual UNION ALL
SELECT 'name2','code1',TO_DATE('07.09.2016','DD.MM.YYYY'),'acc1+curr2' FROM dual UNION ALL
SELECT 'name3','code1',TO_DATE('07.09.2016','DD.MM.YYYY'),'acc2+curr2' FROM dual
)
SELECT
name
,code
,today
,MAX(account1)
,MAX(account2)
,MAX(account3)
,MAX(account4)
,MAX(account5)
,MAX(account6)
,MAX(account7)
,MAX(account8)
FROM
(SELECT
name
,code
,today
,CASE
WHEN rn = 1 THEN account
END account1
,CASE
WHEN rn = 2 THEN account
END account2
,CASE
WHEN rn = 3 THEN account
END account3
,CASE
WHEN rn = 4 THEN account
END account4
,CASE
WHEN rn = 5 THEN account
END account5
,CASE
WHEN rn = 6 THEN account
END account6
,CASE
WHEN rn = 7 THEN account
END account7
,CASE
WHEN rn = 8 THEN account
END account8
FROM
(SELECT
name
,code
,today
,account
,ROW_NUMBER() OVER (PARTITION BY name ORDER BY account) rn
FROM
account_data
)
)
GROUP BY
name
,code
,today
;
UPDATE >>>>>>>>>
The WITH... clause above is just because I don't have your tables and data in my system. I've rewritten my answer using your query as a guide - please note I have not been able to test this ...
SELECT
name
,code
,today
,MAX(account1)
,MAX(account2)
,MAX(account3)
,MAX(account4)
,MAX(account5)
,MAX(account6)
,MAX(account7)
,MAX(account8)
FROM
(SELECT
name
,code
,today
,CASE
WHEN rn = 1 THEN account
END account1
,CASE
WHEN rn = 2 THEN account
END account2
,CASE
WHEN rn = 3 THEN account
END account3
,CASE
WHEN rn = 4 THEN account
END account4
,CASE
WHEN rn = 5 THEN account
END account5
,CASE
WHEN rn = 6 THEN account
END account6
,CASE
WHEN rn = 7 THEN account
END account7
,CASE
WHEN rn = 8 THEN account
END account8
FROM
(SELECT
b.description AS Name
,b.contragentidentifycode AS Code
,c.systemday AS Today
,b.accountno AS Account
,b.currencysname AS Currency
,b.accountno || b.currencysname AS Accounts
,ROW_NUMBER() OVER (PARTITION BY b.description ORDER BY b.accountno) rn
FROM vAACCOUNT b, currentdaysetting c
WHERE b.contragentid = 412
AND b.accountno LIKE '26%'
)
)
GROUP BY
name
,code
,today
;
If you know all the account+currency combinations you can use this pivot (I only implemented 3 of them here):
select *
from (
<your-query> )
pivot (
min(accounts) as accounts FOR (accounts) in ('acc1+curr1' as a, 'acc2+curr1' as b, 'acc1+curr2' c)
);
There is my pivot solution:
SELECT *
FROM (
SELECT id, Name, Code, Today, Account || Currency as Accounts
FROM (
SELECT
row_number() over (ORDER BY b.id) AS id
, b.description AS Name
, b.contragentidentifycode AS Code
, c.systemday AS Today
, b.accountno AS Account
, b.currencysname AS Currency
FROM vAACCOUNT b, currentdaysetting c
WHERE b.contragentid = 412
AND b.accountno LIKE '26%'
)
)
pivot (
MIN(Accounts)
FOR ID IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
) pvt
I have two tables as below. Caseid from first table is referenced in second table along with accidents. What I am trying to get total different accidents for a case type. Below two tables I documented sample data and expected result.
Table case:
caseId CaseType
1 AB
2 AB
3 AB
4 CD
5 CD
6 DE
Table CaseAccidents:
AccidentId caseID AccidentRating
1 1 High
2 1 High
3 1 Medium
4 1 LOW
5 2 High
6 2 Medium
7 2 LOW
8 5 High
9 5 High
10 5 Medium
11 5 LOW
Result should look like:
CaseType TotalHIghrating TotalMediumRating TotalLOWRating
AB 3 2 2
CD 2 1 1
DE 0 0 0
To get the sum of every rating, you can Use a SUM(CASE WHEN) clause, adding 1 by every record that match the rating.
In your question, you have pointed out that you want to see all distinct CaseType, you can get it by using a RIGHT JOIN, this will include all records of case table.
select case.CaseType,
sum(case when caseAccidents.AccidentRating = 'High' then 1 else 0 end) as TotalHighRating,
sum(case when caseAccidents.AccidentRating = 'Medium' then 1 else 0 end) as TotalMediumRating,
sum(case when caseAccidents.AccidentRating = 'LOW' then 1 else 0 end) as TotalLowRating
from caseAccidents
right join case on case.caseId = caseAccidents.caseID
group by case.CaseType;
+----------+-----------------+-------------------+----------------+
| CaseType | TotalHighRating | TotalMediumRating | TotalLowRating |
+----------+-----------------+-------------------+----------------+
| AB | 3 | 2 | 2 |
+----------+-----------------+-------------------+----------------+
| CD | 2 | 1 | 1 |
+----------+-----------------+-------------------+----------------+
| DE | 0 | 0 | 0 |
+----------+-----------------+-------------------+----------------+
Check it: http://rextester.com/MCGJA9193
Have you use case in a select clause before?
select C.CaseType,
sum(case when CA.AccidentRating = 'High' then 1 else 0 end)
from Case C join CaseAccidents CA on C.CaseId = CA.CaseId
group by C.CaseType
Please see this. Sample query of the table and also that result
create table #case(caseid int,casetype varchar(5))
insert into #case (caseid,casetype)
select 1,'AB' union all
select 2,'AB' union all
select 3,'AB' union all
select 4,'CD' union all
select 5,'CD' union all
select 6,'DE'
create table #CaseAccidents(AccidentId int, CaseId int,AccidentRating varchar(10))
insert into #CaseAccidents(AccidentId, CaseId, AccidentRating)
select 1,1,'High' union all
select 2,1,'High' union all
select 3,1,'Medium' union all
select 4,1,'Low' union all
select 5,2,'High' union all
select 6,2,'Medium' union all
select 7,2,'Low' union all
select 8,5,'High' union all
select 9,5,'High' union all
select 10,5,'Medium' union all
select 11,5,'Low'
My script
select c.casetype,
sum(case when ca.AccidentRating='High' then 1 else 0 end) as TotalHighRating,
sum(case when ca.AccidentRating='Medium' then 1 else 0 end) as TotalMediumRating,
sum(case when ca.AccidentRating='Low' then 1 else 0 end) as TotalLowRating
from #case c
Left join #CaseAccidents ca
on c.Caseid=ca.Caseid
group by c.casetype
Hope This could help!
Another approach using Pivot operator
SELECT casetype,
[High],
[Medium],
[Low]
FROM (SELECT c.casetype,
AccidentRating
FROM case c
LEFT JOIN CaseAccidents ca
ON ca.CaseId = c.caseid)a
PIVOT (Count(AccidentRating)
FOR AccidentRating IN ([High],
[Medium],
[Low]) ) p
Try This code once.
select casetype,
sum(case when ca.AccidentRating='High' then 1 else 0 end ) as TotalHIghrating,
sum(case when ca.AccidentRating='Medium' then 1 else 0 end ) as TotalMediumRating ,
sum(case when ca.AccidentRating='Low' then 1 else 0 end ) as TotalLOWRating
from #case c
left join #CaseAccidents ca on c.caseid=ca.CaseId
group by casetype
lets say I have a table containing
|vendor | price| productID|
|--------------------------------|
|abc | 6 | 0001 |
|1 | 7 | 0001 |
|def | 8 | 0001 |
|xyz | 30 | 0002 |
|zxy | 32 | 0002 |
now I want to get the vendor which has the min() price for a product
for product 0001 that would be Vendor abc
for product 0002 that would be Vendor xyz
BUT! IF there is a Vendor named 1 I would like to see his name instead of the actual vendor with the min() price, if there is no Vendor named 1 for a product, I want to see the the one with the min() price again
if that makes any sense for you.. its kinda like a if-else construct but I dont know how to do it in SQL
(sorry for the bad formatted table, I just dont get it formatted the right way)
Thank you
This is a prioritization query. One method is to use row_number() and to put the rules for prioritization into the order by. This resulting query:
select t.*
from (select t.*,
row_number() over (partition by productId
order by (case when vendorid = 1 then 1 else 2 end),
price asc
) as seqnum
from t
) t
where seqnum = 1;
The (currently) accepted answer does not meet the OP's requirement. The requirement was to show the minimum price for each product, even if vendor '1' does not have the lowest price. However, when someone else has the lowest price but vendor '1' also sells the same product, replace the vendor name with '1' (but don't change the lowest price to vendor `1``s price). This looks like "will meet my competitors' lowest price" type of arrangement.
Query and output:
with
price_table ( vendor, price, productid ) as (
select 'abc', 6 , '0001' from dual union all
select '1' , 7 , '0001' from dual union all
select 'def', 8 , '0001' from dual union all
select 'xyz', 30 , '0002' from dual union all
select 'zxy', 32 , '0002' from dual
),
prep ( vendor, price, productid, rn, ct ) as (
select vendor, price, productid,
row_number() over (partition by productid order by price),
count( case when vendor = '1' then 1 end ) over ( partition by productid)
from price_table
)
select case when ct = 0 then vendor else '1' end as vendor,
price,
productid
from prep
where rn = 1
;
VENDOR PRICE PROD
------ ------- ----
1 6 0001
xyz 30 0002