I am wondering how to write this query.
I know this actual syntax is bogus, but it will help you understand what I want.
I need it in this format, because it is part of a much bigger query.
SELECT distributor_id,
COUNT(*) AS TOTAL,
COUNT(*) WHERE level = 'exec',
COUNT(*) WHERE level = 'personal'
I need this all returned in one query.
Also, it need to be in one row, so the following won't work:
'SELECT distributor_id, COUNT(*)
GROUP BY distributor_id'
You can use a CASE statement with an aggregate function. This is basically the same thing as a PIVOT function in some RDBMS:
SELECT distributor_id,
count(*) AS total,
sum(case when level = 'exec' then 1 else 0 end) AS ExecCount,
sum(case when level = 'personal' then 1 else 0 end) AS PersonalCount
FROM yourtable
GROUP BY distributor_id
One way which works for sure
SELECT a.distributor_id,
(SELECT COUNT(*) FROM myTable WHERE level='personal' and distributor_id = a.distributor_id) as PersonalCount,
(SELECT COUNT(*) FROM myTable WHERE level='exec' and distributor_id = a.distributor_id) as ExecCount,
(SELECT COUNT(*) FROM myTable WHERE distributor_id = a.distributor_id) as TotalCount
FROM (SELECT DISTINCT distributor_id FROM myTable) a ;
EDIT:
See #KevinBalmforth's break down of performance for why you likely don't want to use this method and instead should opt for #Taryn♦'s answer. I'm leaving this so people can understand their options.
SELECT
distributor_id,
COUNT(*) AS TOTAL,
COUNT(IF(level='exec',1,null)),
COUNT(IF(level='personal',1,null))
FROM sometable;
COUNT only counts non null values and the DECODE will return non null value 1 only if your condition is satisfied.
Building on other posted answers.
Both of these will produce the right values:
select distributor_id,
count(*) total,
sum(case when level = 'exec' then 1 else 0 end) ExecCount,
sum(case when level = 'personal' then 1 else 0 end) PersonalCount
from yourtable
group by distributor_id
SELECT a.distributor_id,
(SELECT COUNT(*) FROM myTable WHERE level='personal' and distributor_id = a.distributor_id) as PersonalCount,
(SELECT COUNT(*) FROM myTable WHERE level='exec' and distributor_id = a.distributor_id) as ExecCount,
(SELECT COUNT(*) FROM myTable WHERE distributor_id = a.distributor_id) as TotalCount
FROM myTable a ;
However, the performance is quite different, which will obviously be more relevant as the quantity of data grows.
I found that, assuming no indexes were defined on the table, the query using the SUMs would do a single table scan, while the query with the COUNTs would do multiple table scans.
As an example, run the following script:
IF OBJECT_ID (N't1', N'U') IS NOT NULL
drop table t1
create table t1 (f1 int)
insert into t1 values (1)
insert into t1 values (1)
insert into t1 values (2)
insert into t1 values (2)
insert into t1 values (2)
insert into t1 values (3)
insert into t1 values (3)
insert into t1 values (3)
insert into t1 values (3)
insert into t1 values (4)
insert into t1 values (4)
insert into t1 values (4)
insert into t1 values (4)
insert into t1 values (4)
SELECT SUM(CASE WHEN f1 = 1 THEN 1 else 0 end),
SUM(CASE WHEN f1 = 2 THEN 1 else 0 end),
SUM(CASE WHEN f1 = 3 THEN 1 else 0 end),
SUM(CASE WHEN f1 = 4 THEN 1 else 0 end)
from t1
SELECT
(select COUNT(*) from t1 where f1 = 1),
(select COUNT(*) from t1 where f1 = 2),
(select COUNT(*) from t1 where f1 = 3),
(select COUNT(*) from t1 where f1 = 4)
Highlight the 2 SELECT statements and click on the Display Estimated Execution Plan icon. You will see that the first statement will do one table scan and the second will do 4. Obviously one table scan is better than 4.
Adding a clustered index is also interesting. E.g.
Create clustered index t1f1 on t1(f1);
Update Statistics t1;
The first SELECT above will do a single Clustered Index Scan. The second SELECT will do 4 Clustered Index Seeks, but they are still more expensive than a single Clustered Index Scan. I tried the same thing on a table with 8 million rows and the second SELECT was still a lot more expensive.
For MySQL, this can be shortened to:
SELECT distributor_id,
COUNT(*) total,
SUM(level = 'exec') ExecCount,
SUM(level = 'personal') PersonalCount
FROM yourtable
GROUP BY distributor_id
Well, if you must have it all in one query, you could do a union:
SELECT distributor_id, COUNT() FROM ... UNION
SELECT COUNT() AS EXEC_COUNT FROM ... WHERE level = 'exec' UNION
SELECT COUNT(*) AS PERSONAL_COUNT FROM ... WHERE level = 'personal';
Or, if you can do after processing:
SELECT distributor_id, COUNT(*) FROM ... GROUP BY level;
You will get the count for each level and need to sum them all up to get the total.
I do something like this where I just give each table a string name to identify it in column A, and a count for column. Then I union them all so they stack. The result is pretty in my opinion - not sure how efficient it is compared to other options but it got me what I needed.
select 'table1', count (*) from table1
union select 'table2', count (*) from table2
union select 'table3', count (*) from table3
union select 'table4', count (*) from table4
union select 'table5', count (*) from table5
union select 'table6', count (*) from table6
union select 'table7', count (*) from table7;
Result:
-------------------
| String | Count |
-------------------
| table1 | 123 |
| table2 | 234 |
| table3 | 345 |
| table4 | 456 |
| table5 | 567 |
-------------------
Based on Taryn's response with an added nuance using OVER():
SELECT distributor_id,
COUNT(*) total,
SUM(case when level = 'exec' then 1 else 0 end) OVER() ExecCount,
SUM(case when level = 'personal' then 1 else 0 end) OVER () PersonalCount
FROM yourtable
GROUP BY distributor_id
Using OVER() with nothing in the () will give you the total count for the whole dataset.
I think this can also works for you select count(*) as anc,(select count(*) from Patient where sex='F')as patientF,(select count(*) from Patient where sex='M') as patientM from anc
and also you can select and count related tables like this select count(*) as anc,(select count(*) from Patient where Patient.Id=anc.PatientId)as patientF,(select count(*) from Patient where sex='M') as patientM from anc
In Oracle you'll do something like
SELECT
(SELECT COUNT(*) FROM schema.table1),
(SELECT COUNT(*) FROM schema.table2),
...
(SELECT COUNT(*) FROM schema.tableN)
FROM DUAL;
If your flavor of SQL supports it, you can use COUNT_IF() to count based on a condition.
SELECT
distributor_id,
COUNT(*) AS total_count,
COUNT_IF(level = 'exec') AS exec_count,
COUNT_IF(level = 'personal') AS personal_count
FROM table_name
GROUP BY distributor_id
The recently added PIVOT functionality can do exactly what you need:
SELECT *
FROM ( SELECT level from your_table )
PIVOT ( count(*) for level in ('exec', 'personal') )
Related
I have a union of three tables (t1, t2, t3).
Each rerun exactly the same number of records, first column is id, second amount:
1 10
2 20
3 20
1 30
2 30
3 10
1 20
2 40
3 50
Is there a simple way in SQL to sum it up, i.e. to only get:
1 60
2 80
3 80
select id, sum(amount) from (
select id,amount from table_1 union all
select id,amount from table_2 union all
select id,amount from table_3
) x group by id
SELECT id, SUM(amount) FROM
(
SELECT id, SUM(amount) AS `amount` FROM t1 GROUP BY id
UNION ALL
SELECT id, SUM(amount) AS `amount` FROM t2 GROUP BY id
) `x`
GROUP BY `id`
I groupped each table and unioned because i think it might be faster, but you should try both solutions.
Subquery:
SELECT id, SUM(amount)
FROM ( SELECT * FROM t1
UNION ALL SELECT * FROM t2
UNION ALL SELECT * FROM t3
)
GROUP BY id
Not sure if MySQL uses common table expression but I would do this in postgres:
WITH total AS(
SELECT id,amount AS amount FROM table_1 UNION ALL
SELECT id,amount AS amount FROM table_2 UNION ALL
SELECT id,amount AS amount FROM table_3
)
SELECT id, sum(amount)
FROM total
I think that should do the trick as well.
As it's not very clear from previous answers, remember to give aliases (on MySQL/MariaDb) or you'll get error:
Every derived table must have its own alias
select id, sum(amount) from (
select id,amount from table_1 union all
select id,amount from table_2 union all
select id,amount from table_3
) AS 'aliasWhichIsNeeded'
group by id
Yes!!! Its okay! Thanks!!!!
My code finishing:
SELECT SUM(total)
FROM (
(SELECT 1 as id, SUM(e.valor) AS total FROM entrada AS e)
UNION
(SELECT 1 as id, SUM(d.valor) AS total FROM despesa AS d)
UNION
(SELECT 1 as id, SUM(r.valor) AS total FROM recibo AS r WHERE r.status = 'Pago')
) x group by id
SELECT BANKEMPNAME, workStation, SUM (CALCULATEDAMOUNT) FROM(
SELECT BANKEMPNAME, workStation, SUM(CALCULATEDAMOUNT) AS CALCULATEDAMOUNT,SALARYMONTH
FROM dbo.vw_salaryStatement
WHERE (ITEMCODE LIKE 'A%')
GROUP BY BANKEMPNAME,workStation, SALARYMONTH
union all
SELECT BANKEMPNAME, workStation, SUM(CALCULATEDAMOUNT) AS CALCULATEDAMOUNT,SALARYMONTH
FROM dbo.vw_salaryStatement
WHERE (ITEMCODE NOT LIKE 'A%')
GROUP BY BANKEMPNAME, workStation, SALARYMONTH) as t1
WHERE SALARYMONTH BETWEEN '20220101' AND '20220131'
group by BANKEMPNAME, workStation
order by BANKEMPNAME asc
IN MSSQL You can write this way, But Doing UNION ALL THE Column should be the same for both ways.
I have given this example So that you can understand the process...
I have a table looking like this:
ID quote_no version
------------------------
1 123 1
2 123 2
3 123 1
4 123 2
5 321 1
6 321 1
I would like to select the latest version of each quote, and if theres multiple records of that version i would like to get the row with the highest ID.
(in this case the query should produce the following result):
ID quote_no version
------------------------
4 123 2
6 321 1
How could I do that in a query?
You can approach this with a not exists clause:
select t.*
from table t
where not exists (select 1
from table t2
where t2.quote_no = t.quote_no and
(t2.version > t.version or
t2.version = t.version and t2.id > t.id
)
);
If you just want the one with the highest id (which is also consistent with your results), you can do:
select t.*
from table t join
(select quote_no, max(id) as maxid
from table t
group by quote_no
) tt
on t.id = tt.maxid;
I would write a subquery that gets the largest version for each quote_no, like this:
SELECT quote_no, MAX(version) AS maxVersion
FROM myTable
GROUP BY quote_no;
And you can join that with your original table, and use another MAX() function to get the largest id:
SELECT MAX(m.id), m.quote_no, mt.maxVersion
FROM myTable m
JOIN(
SELECT quote_no, MAX(version) AS maxVersion
FROM myTable
GROUP BY quote_no) mt ON mt.quote_no = m.quote_no AND mt.maxVersion = m.version
GROUP BY m.quote_no;
Works fine in SQL Fiddle.
The latest X records per group is a tough problem in MySQL, except when X is 1 as in your case. You can do it like this. For each quote, join all rows that have the same quote_no, but a greater version., or the same version but with a larger ID. Then you can apply a filter to only keep those rows that don't have a greater version:
SELECT
t1.*
FROM
YourTable t1
LEFT JOIN YourTable t2 ON
t2.quote_no = t1.quote_no AND -- Quote must match anyway
( t2.version > t1.version OR -- Version must be larger
( t2.version = t1.version AND -- Or if version is the same...
t2.ID > t1.ID ) -- ID must be larger.
WHERE
t2.quote_no IS NULL
Try something like that :
SELECT t2.*
FROM QuoteTable t2
JOIN (SELECT
MAX(ID) AS t1.LatestId,
quote_no
FROM QuoteTable t1
GROUP BY quote_no)
ON t2.id = t1.LatestId;
EDIT : the Parent SELECT statement prevents version number error.
I want to get count of a row it exists in table.
+---+----+
|id |name|
+---+----+
|100|a |
+---+----+
|201|b |
+---+----+
|302|c |
+---+----+
|403|d |
+---+----+
|504|e |
+---+----+
In the above table i want to get output as 4(i.e) the count of that row exists. I have 'd' value and have to write a query to get the output as 4 where name = d
I think code will be something like the below,
select count(*) ......
If i'm correctly understand you this query is what you want:
set #row_number = 0;
select #row_number := #row_number + 1 as row_number,name FROM table_name;
SELECT ROW_NUMBER() OVER(ORDER BY id) FROM yourtable WHERE name='d'
MySQL version
SET #rank=0;
SELECT #rank := #rank+1 AS rank
FROM yourtable WHERE name='d'
ORDER BY id asc
#vinoth I think #Pragmatist Answer would work for you. Just add this clause to his query :
set #row_number = 0;
select #row_number := #row_number + 1 as row_number,name FROM table_name Where name='b';
Try the below. MS SQL is a tested query, i just converted into mysql. Hope you can modify as per your requirement.
SET #rank=0;
SELECT * FROM Table1 T1
INNER JOIN(
SELECT #rank := #rank+1 AS rank, ID
FROM Table1
ORDER BY id asc) temp ON temp.ID = T1.ID
WHERE T1.name = 'd'
MS SQL Query will be
SELECT * FROM Table1 T1
INNER JOIN(SELECT id, ROW_NUMBER() OVER (Order by id) AS RowNumber from Table1) temp ON temp.ID = T1.ID
WHERE T1.name = 'd'
To get the Row Number
SELECT ROW_NUMBER() OVER(ORDER BY id) from table WHERE name='d'
To get the row count with your condition use below query
select count(*) as count from table where name = 'b'
Supposedly I have this sql result in PHP ($myresults = mysql_fetch_… either assoc/row/array):
SELECT table1.name as name, sum(table2.numbers) as numbers FROM table2 INNER JOIN
table1 ON table2.fk_id = table1.id GROUP BY name ORDER BY numbers DESC
---------------
| John | 800 |
---------------
| Mark | 500 |
---------------
| Bill | 300 |
---------------
So I am logged as Mark ($_SESSION['name'] == "Mark") and I want to know in which row # the value 'Mark' is located (in this case, row number 1, considering the first row is 0).
How to I get that via PHP?
Thanks…
EDIT: think of it as a High Score or Leaderboards table, I don't need the user id, but the row in which the user is located as of right now…
You should use user defined variables this way:
SELECT table1.name as name, sum(table2.numbers) as numbers,
#rank := #rank + 1 rank
FROM table2
CROSS JOIN (SELECT #rank := 0) init
JOIN table1 ON table2.fk_id = table1.id
GROUP BY name
ORDER BY numbers DESC
After a second thought, the group by might give you some trouble with the counting of the UDVs. This is another alternative but will be less performant than the previous approach.
SELECT *, #rank := #rank + 1 rank FROM (
SELECT table1.name as name, sum(table2.numbers) as numbers
FROM table2
JOIN table1 ON table2.fk_id = table1.id
GROUP BY name
) s
CROSS JOIN (SELECT #rank := 0) init
ORDER BY numbers DESC
Anyway, I would recommend counting directly in PHP. That will be more flexible and performant.
Modify your SQL to select primary ID along with the other data:
SELECT
table1.id as id,
table1.name as name,
sum(table2.numbers) as numbers
FROM
table2
INNER JOIN
table1 ON table2.fk_id = table1.id
GROUP BY
name
ORDER BY
numbers DESC
I have a MySQL table with the following structure:
I want a query that would receive a group of uids (or a single uid) and then check for their existence in a closed group under a specific mid. If they exist, the query should return the mid under which they exist. For example in the table above:
('chuks.obima', 'crackhead') should return '2
('vweetah','crackhead') should return '1'
('vweetah','crackhead','chuks.obima') should return 3
('crackhead') should return an empty result
I think you need something like this:
SELECT mid
FROM your_table
WHERE uid in ('favour','crackhead','charisma')
GROUP BY mid
HAVING COUNT(*)=3
EDIT: based on your second example, this is what you are looking for:
SELECT mid
FROM your_table
WHERE uid in ('vweetah', 'crackhead')
GROUP BY mid
HAVING
COUNT(distinct uid)=
(select count(*)
from (select 'vweetah' union select 'crackhead') s)
or you can just substitute last subquery with the number of elements you are looking for, e.g. HAVING COUNT(distinct uid) = 2
EDIT2: now i understand exactly what you are looking for. This should give you the correct results:
SELECT your_table.mid, s.tot_count, count(distinct uid)
FROM
your_table inner join
(select mid, seq, count(distinct uid) tot_count from your_table group by mid, seq) s
on your_table.mid = s.mid and your_table.seq=s.seq
WHERE your_table.uid in ('crackhead')
GROUP BY your_table.mid
HAVING COUNT(distinct uid)=s.tot_count AND COUNT(distinct uid)=1
where the last count is equal to the number of elements you are looking for. This could be simplified like this:
SELECT your_table.mid
FROM your_table
GROUP BY your_table.mid
HAVING
count(distinct uid)=
count(distinct case when your_table.uid in ('vweetah','crackhead','chuks.obima') then your_table.uid end)
and count(distinct uid)=3
If the group is to considered closed if all uid are under the same seq, you also have to modify group by with: group by your_table.mid, your_table.seq and your select with SELECT distinct your_table.mid
To verify that it is a closed group, you can get the aggregate COUNT() of the total members of that mid group and compare it to the number of people in your list. If they are equal, it is closed.
The following would return a 1 if all 3 are in the group, and the total number of people in the group is also 3.
SELECT
(((SELECT COUNT(*) FROM yourtable WHERE `uid` IN ('favour','crackhead','charisma') AND `mid` = 2)
=
(SELECT COUNT(*) FROM yourtable WHERE `mid` = 2))
AND (SELECT COUNT(*) FROM yourtable WHERE `mid` = 2) = 3) AS group_is_closed
Wrap it in a subquery to avoid counting the mid twice.
SELECT
/* 3 is the number of uid you are looking for */
(mid_count = 3 AND mid_count = member_count) AS group_is_closed
FROM (
SELECT
/* Find how many of your uids are in the `mid` */
(SELECT COUNT(*) FROM yourtable WHERE `uid` IN ('favour','crackhead','charisma') AND `mid` = 2) AS member_count,
/* Find the total number of uids in the `mid` */
(SELECT COUNT(*) FROM yourtable WHERE `mid` = 2) AS mid_count
) subq
SQLFiddle demos (aka wow, it actually works):
Positive result (Only the 3 selected are in the mid, returns 1)
Negative result (A user not among the 3 is also in the mid, returns 0)
Negative result 2 (One of the 3 users is not in the mid, returns 0)
Try this:
SELECT mid
FROM your_table
WHERE uid in ('favour','crackhead','charisma')
GROUP BY mid
HAVING COUNT(DISTINCT uid) = 3