How Can i count multple columns? - sql-server-2008

Here is my Table
I want to count reference number and per permit ex. BP, AP, OP
My Sample Query
SELECT
C.CompanyName,
A.ReferenceNumber,
Count(CR.BP,
CR.AP,
CR.SP,
CR.FP,
CR.EP,
CR.ELP,
CR.MP,
CR.EGPP,
CR.OP,
CR.EPTO,
CR.ELPTO,
CR.MPTO,
CR.FSIC,
CR.AI,
CR.EPTO_R,
CR.ELPTO_R,
CR.MPTO_R,
CR.LOA)
From Companies AS C
inner join ApplicationDetails as A on(C.CompanyId = A.CompanyID)
inner join CertificateRecords as CR on(A.ReferenceNumber = CR.ReferenceNumber)
group by A.ReferenceNumber
ERROR is The Count function requires 1 argument(s).

Count refers to rows, not columns.
There for, it makes no sense to use it on multiple columns.
You can either use a column name, an asterisk or a constant (the last 2 are equivalent).
The reason for these options is that count ignores null values - so you can use it in 2 different ways.
When using a column name, you will get the number of values in that column (per group, if you are using it with group by. Note that null is not considered a value here, so if your column is nullable, you'll get only the number of rows where it's not null.
When using asterisk or a constant, count will not ignore null values, even if all the columns in a row are contains null - that row will still get counted.
Here is a quick demo I've made to illustrate it:
DECLARE #T AS TABLE
(
col1 int NULL,
col2 int NULL
)
INSERT INTO #T VALUES
(1,1),
(1,2),
(1,3),
(1, NULL),
(2,1),
(2,2),
(2,3),
(2, NULL),
(NULL, 1),
(NULL, 2),
(NULL, 3),
(NULL, NULL)
Count with all options:
SELECT col1,
COUNT(col2) As ColumnName,
COUNT(*) Asterisk,
COUNT(1) Constant
FROM #T
GROUP BY col1
Results: (note that the column with the column name ignores the null rows)
col1 ColumnName Asterisk Constant
NULL 3 4 4
1 3 4 4
2 3 4 4
Compare count(*) with count(1):
SELECT COUNT(*) Asterisk,
COUNT(1) Constant
FROM #T
Results:
Asterisk Constant
12 12

Related

SQL: Can I refer/access data the current row in a window function?

Here is an example. Suppose I have the following table:
id | list
--------+----------
10 |
| 10,20
20 | 10,20
For each id value, I'd like to calculate the number of rows having a list value which contains that id value. The result would be look like that:
id | count of lists
--------+----------
10 | 2
| 0
20 | 2
I suggest a window function should be used, but it seems that I can't access the id value from within such a function.
I totally agree that it is BAD design. This question is about the possibility.
Any MySQL/PostgreSQL solution is fine.
Assuming that you are using MySQL, and assuming that your table has the name test, and assuming that both columns are string types:
SELECT
t1.id, count(t2.list)
FROM
(test t1 LEFT JOIN test t2 ON
(t2.list LIKE CONCAT('%,', t1.id, ',%')) OR
(t2.list LIKE CONCAT('%,', t1.id)) OR
(t2.list LIKE CONCAT(t1.id, ',%')))
GROUP BY t1.id;
Please be aware that this solution might be very slow depending on the number of records you have and depending on the average length of the strings in the list field.
If you need something faster, I think it couldn't be a single query. Perhaps we would have to write a stored procedure or some application logic for that, or use additional tables or columns and a series of multiple SQL statements.
Before I start, as mentioned above, this is a poor design. However, this is how I would query it:
CREATE TABLE #Lists (id int, list varchar(500));
INSERT INTO #Lists (id, list) VALUES
(10, NULL), (NULL, '10,20'), (20, '10,20');
WITH cte AS (
SELECT LEFT(list, INSTR(list, '%,%')-1) AS value, SUBSTRING(list, INSTR(list, '%,%') + 1, 500) AS list FROM #Lists
UNION ALL
SELECT CASE WHEN list LIKE ('%,%') THEN LEFT(list, INSTR(list, '%,%')-1) ELSE list END AS value, CASE WHEN list LIKE ('%,%') THEN SUBSTRING(list, INSTR(list, '%,%') + 1, 500) END AS list FROM cte
WHERE CHAR_LENGTH(list) > 0
)
SELECT value, COUNT(*) FROM cte GROUP BY value;
DROP TABLE #Lists;
This solution allows for any number of values in the list string (like '10,20,30').
Ideally, the list values should be stored in a separate table so that each record has a single value, such as CREATE TABLE BetterDesign (id int, value int) INSERT INTO BetterDesign (id, value) VALUES (10, NULL), (NULL, 10), (NULL, 20), (20, 10), (20, 20). Along with a million other reasons, this is better for querying SELECT value, COUNT(*) FROM BetterDesign GROUP BY value. That being said, I understand the pains of legacy systems.

Trying to remove duplicate entries in MySQL database with a twist

First off, I'm a nooob. I've got a Database Table with lots of duplicate rows. I created a new clean database table (ufx_multicity) and tried the following command in MySQL to copy all the rows from the old database table (ufx_multicity2) and to ignore the duplicate entries.
INSERT INTO ufx_multicity
(`country_id`,
`zones_id`,
`cityname`,
`city_slug`,
`lat`,
`lng`,
`scall_factor`,
`is_zoom_home`,
`map_type`,
`post_type`,
`categories`,
`is_default`,
`message`,
`color`,
`images`,
`header_color`,
`header_image`,
`cat_scall_factor`,
`is_zoom_cat`)
SELECT DISTINCT `cityname`,
`city_slug`,
`lat`,
`lng`
FROM ufx_multicity2;
I get the following error:
1136 - Column count doesn't match value count at row 1
How do I fix this? Your help is appreciated.
You are trying to insert into 19 columns:
INSERT INTO ufx_multicity (country_id, zones_id, cityname, city_slug, lat, lng, scall_factor, is_zoom_home, map_type, post_type, categories, is_default, message, color, images, header_color, header_image, cat_scall_factor, is_zoom_cat)
data from only 4 columns
SELECT DISTINCT cityname, city_slug, lat, lng FROM ufx_multicity2;
The two must match so you have 4 columns inserted into 4 columns:
INSERT INTO ufx_multicity (cityname, city_slug, lat, lng) SELECT DISTINCT cityname, city_slug, lat, lng FROM ufx_multicity2;
Column count doesn't match value count
Isn't this error message pretty descriptive?
Your INSERT is naming 19 columns that it expects you to provide data for.
But the SELECT only names four columns. How is the INSERT supposed to know which four of the nineteen columns to put data into? And what values should it use for the other fifteen columns?
The number of columns must be the same, and in the same order. Either name fewer columns in the INSERT, or else provide more columns in the SELECT.
Re your question about duplicates:
You might misunderstand how SELECT DISTINCT works. It means that a row counts as distinct if any column in the row is different from the columns in another row. And DISTINCT always applies includes all of the columns of the select-list in this determination.
You could try using GROUP BY instead of DISTINCT.
INSERT INTO wfx_multicity (country_id, zones_id, cityname,
city_slug, lat, lng,
scall_factor, is_zoom_home, map_type, post_type, categories,
is_default, message, color, images, header_color)
SELECT MAX(country_id), MAX(zones_id), cityname, city_slug, lat, lng,
MAX(scall_factor), MAX(is_zoom_home), MAX(map_type), MAX(post_type), MAX(categories),
MAX(is_default), MAX(message), MAX(color), MAX(images), MAX(header_color)
FROM wfx_multicity2
GROUP BY cityname, city_slug, lat, lng;
GROUP BY works differently. It reduces the result of the SELECT to one row for each distinct combination of the columns named in the GROUP BY. This gives you the chance to choose a subset of columns for reducing the rows to those with distinct values over just those few columns.
Notice I put the grouping function MAX() around each of the other columns. If you don't do this, MySQL chooses values for those columns arbitrarily from rows in each group.
Consider this table:
col1 col2 col3
---- ---- ----
1 14 'X'
1 12 'Z'
1 14 'Y'
2 27 'K'
2 9 'K'
3 6 'Q'
If I query from this table like so:
SELECT col1, col2, col3 FROM mytable GROUP BY col1;
It should return only three rows, because there are three distinct values in col1. But what should it return in the other columns? It's ambiguous. In practice, MySQL might return the values from the first row in each group, but it makes no guarantee of doing this consistently.
In strict SQL, the ambiguous form of the query is an error (and for what it's worth, MySQL 5.7 will make it an error too).
SELECT col1, MAX(col2), MAX(col3) FROM mytable GROUP BY col1;
Using a grouping function like MAX() resolves the ambiguity, but does this return all the values from the last row in each group? Not really -- it returns the largest value in each column, even if those values come from different rows.
col1 MAX(col2) MAX(col3)
---- --------- ---------
1 14 'Z' <-- none of the rows has this combination
2 27 'K'
3 6 'Q'
So the next question you need to ask is, which row do I want to pick rows from? Or does it matter?

SQL Query to get number of entries

I am trying to select a random number of entries based on a specified parameter.
Like If I have a table T
T( id, data1, data2 ,no);
Now no is a field with a random bunch of numbers.
I want to get a random subset of T such that the number of no is at a particular value.
For example lets say I want total no=7
T(0,a,a,4);
T(1,B,B,4);
T(2,v,v,1);
T(3,d,d,2);
T(4,d,d,3);
The output of the query to the query would be either
T(0,a,a,4);
T(4,d,d,3);
OR
T(1,B,B,4);
T(2,v,v,1);
T(3,d,d,2);
etc.
Is this possible? I couldn't think of a logic. The best I could think of was retrieving them one row at a time and keep counting no but that's inefficient.
(NOTE: If the count exceeds no it is acceptable but not the other way around)
This will get you pretty close.
create table t ( name varchar(1), num tinyint ) ;
insert into t (name, num) values ( 'a', 4 ), ('b', 2), ('c', 3),
('d',1), ('e', 4), ('f', 1),('g', 6);
Here's the query:
select * from
(select name, num, #cum := #cum + num as cumulate from
(select * from t order by rand()) as t3,
(select #cum := 0 ) as t1
) as t2
where cumulate <= 7;
Here's a fiddle. Im sure it can be optimized but I was curious to see how to create it.

PostgreSQL equivalent for MySQL GROUP BY

I need to find duplicates in a table. In MySQL I simply write:
SELECT *,count(id) count FROM `MY_TABLE`
GROUP BY SOME_COLUMN ORDER BY count DESC
This query nicely:
Finds duplicates based on SOME_COLUMN, giving its repetition count.
Sorts in desc order of repetition, which is useful to quickly scan major dups.
Chooses a random value for all remaining columns, giving me an idea of values in those columns.
Similar query in Postgres greets me with an error:
column "MY_TABLE.SOME_COLUMN" must appear in the GROUP BY clause or be
used in an aggregate function
What is the Postgres equivalent of this query?
PS: I know that MySQL behaviour deviates from SQL standards.
Back-ticks are a non-standard MySQL thing. Use the canonical double quotes to quote identifiers (possible in MySQL, too). That is, if your table in fact is named "MY_TABLE" (all upper case). If you (more wisely) named it my_table (all lower case), then you can remove the double quotes or use lower case.
Also, I use ct instead of count as alias, because it is bad practice to use function names as identifiers.
Simple case
This would work with PostgreSQL 9.1:
SELECT *, count(id) ct
FROM my_table
GROUP BY primary_key_column(s)
ORDER BY ct DESC;
It requires primary key column(s) in the GROUP BY clause. The results are identical to a MySQL query, but ct would always be 1 (or 0 if id IS NULL) - useless to find duplicates.
Group by other than primary key columns
If you want to group by other column(s), things get more complicated. This query mimics the behavior of your MySQL query - and you can use *.
SELECT DISTINCT ON (1, some_column)
count(*) OVER (PARTITION BY some_column) AS ct
,*
FROM my_table
ORDER BY 1 DESC, some_column, id, col1;
This works because DISTINCT ON (PostgreSQL specific), like DISTINCT (SQL-Standard), are applied after the window function count(*) OVER (...). Window functions (with the OVER clause) require PostgreSQL 8.4 or later and are not available in MySQL.
Works with any table, regardless of primary or unique constraints.
The 1 in DISTINCT ON and ORDER BY is just shorthand to refer to the ordinal number of the item in the SELECT list.
SQL Fiddle to demonstrate both side by side.
More details in this closely related answer:
Select first row in each GROUP BY group?
count(*) vs. count(id)
If you are looking for duplicates, you are better off with count(*) than with count(id). There is a subtle difference if id can be NULL, because NULL values are not counted - while count(*) counts all rows. If id is defined NOT NULL, results are the same, but count(*) is generally more appropriate (and slightly faster, too).
Here's another approach, uses DISTINCT ON:
select
distinct on(ct, some_column)
*,
count(id) over(PARTITION BY some_column) as ct
from my_table x
order by ct desc, some_column, id
Data source:
CREATE TABLE my_table (some_column int, id int, col1 int);
INSERT INTO my_table VALUES
(1, 3, 4)
,(2, 4, 1)
,(2, 5, 1)
,(3, 6, 4)
,(3, 7, 3)
,(4, 8, 3)
,(4, 9, 4)
,(5, 10, 1)
,(5, 11, 2)
,(5, 11, 3);
Output:
SOME_COLUMN ID COL1 CT
5 10 1 3
2 4 1 2
3 6 4 2
4 8 3 2
1 3 4 1
Live test: http://www.sqlfiddle.com/#!1/e2509/1
DISTINCT ON documentation: http://www.postgresonline.com/journal/archives/4-Using-Distinct-ON-to-return-newest-order-for-each-customer.html
mysql allows group by to omit non-aggregated selected columns from the group by list, which it executes by returning the first row found for each unique combination of grouped by columns. This is non-standard SQL behaviour.
postgres on the other hand is SQL standard compliant.
There is no equivalent query in postgres.
Here is a self-joined CTE, which allows you to use select *. key0 is the intended unique key, {key1,key2} are the additional key elements needed to address the currently non-unique rows. Use at your own risk, YMMV.
WITH zcte AS (
SELECT DISTINCT tt.key0
, MIN(tt.key1) AS key1
, MIN(tt.key2) AS key2
, COUNT(*) AS cnt
FROM ztable tt
GROUP BY tt.key0
HAVING COUNT(*) > 1
)
SELECT zt.*
, zc.cnt AS cnt
FROM ztable zt
JOIN zcte zc ON zc.key0 = zt.key0 AND zc.key1 = zt.key1 AND zc.key2 = zt.key2
ORDER BY zt.key0, zt.key1,zt.key2
;
BTW: to get the intended behaviour for the OP, the HAVING COUNT(*) > 1 clause should be omitted.

sql question Select * From * where one=two order by

I need some help with a query.
i want to select from a table, some values, but the values depend on the value of an other cell. after i select i need to sort them.
Can i use ELECT column FROM table WHERE one=two ORDER BY ...?
thanks,
Sebastian
Yes you can, as long as you spell SELECT correctly.
Here is an example you can copy and paste into your MySQL Query Browser to see a query of this type in action:
CREATE TABLE table1 (
id INT NOT NULL,
name1 VARCHAR(100) NOT NULL,
name2 VARCHAR(100) NOT NULL,
sortorder INT NOT NULL
);
INSERT INTO table1 (id, name1, name2, sortorder) VALUES
(1, 'Foo', 'Foo', 4),
(2, 'Boo', 'Unknown', 2),
(3, 'Bar', 'Bar', 3),
(4, 'Baz', 'Baz', 1);
SELECT id
FROM table1
WHERE name1 = name2
ORDER BY sortorder;
Result:
4
3
1
Maybe some working examples will help:
This returns over 8100 records from one of my databases:
SELECT * FROM fax_logs WHERE fee = service_charge
This returns over 2700 records from my data:
SELECT * FROM fax_logs WHERE fee = service_charge + 5
This returns over 6900 records:
SELECT * FROM fax_logs WHERE fee = service_charge + copies
I might misunderstood your question, but I think you are trying to compare values of the first and second column. In Mysql, you can refer columns by number, not by name, only inside ORDER BY clause:
SELECT * FROM table ORDER BY 1 (order by the first column). You cannot use column index in WHERE.