mysql count of distinct records after match - mysql

Given table 'x':
Source Dest Type
A B 2
A D 2
B C 2
Now I want the total count of Source and destination removing the matching ones..
Example of above one: For type 2, Count will be 4, i.e. Count(A,B,C,D)
I tried this:
select Count(distinct Source), Count(distinct destination),Count(distinct source)+Count( distinct destination),Type
from X
where Type=2 and Src NOT IN (select destination
from X
where Type=2)
I need to simplify this query for all the types.
Let me know if there is any way I could do it.
Thanks!

SELECT COUNT(DISTINCT a), Type
FROM (
SELECT DISTINCT Source AS a, Type
FROM x
UNION ALL
SELECT DISTINCT Dest AS a, Type
FROM x
)
GROUP By Type
The inner union query converts your two separate columns into a single column, then the outer query takes that union'd result and counts up the individual values, grouped by Type.

An inefficient method would be to do the following
SELECT
distinctMatches.type AS type,
COUNT(DISTINCT distinctMatches.name) AS quantity
FROM
(
(
SELECT DISTINCT
type,
source AS name
FROM x
)
UNION DISTINCT
(
SELECT DISTINCT
type,
destination AS name
FROM x
)
) AS distinctMatches
GROUP BY distinctMatches.type
This would give with your example data 1 row with type 2 and quantity 4
In an ideal world I'd look at the database design as it feels as if it should be split into multiple tables i.e. one for sources (as you seem to be grouping "source" and "destination" which indicates they are effectively the same item). If you are able to normalise the design further this type of query will be easier to do more efficiently

try this:
SELECT COUNT(xxx.a) as TotalCount
FROM
(SELECT source a, type FROM tableX
UNION
SELECT dest a, type FROM tableX) xxx
WHERE xxx.Type = 2

Related

How to obtain all rows in MySQL table where a column's value is the same?

I am a SQL novice here. Suppose we have the following table:
id type
1 A
2 A
3 B
4 B
5 C
6 C
7 D
8 D
...
Let's say that I do not know the possible values under the column 'type' and assume that it can be one of N number of possible values. In MySQL, how can I form a query to obtain all rows for a single type (either randomly select one type or take the first alphabetically sorted type)?
Desired output:
id type
1 A
2 A
or
id type
5 C
6 C
or
id type
7 D
8 D
You can choose a random type and get all the rows using:
select t.*
from t
where t.type = (select t2.type from t t2 order by rand() limit 1);
You could use RANK() here, for a MySQL 8+ solution:
WITH cte AS (
SELECT *, RANK() OVER (ORDER BY type) rnk
FROM yourTable
)
SELECT id, type
FROM cte
WHERE rnk = 1;
This approach would return all records for the alphabetically lowest type value.
If you want to get first type sorted alphabatically then use min(or use max for last type) as follows:
select t.*
from your_table t
where t.type = (select min(tt.type) from your_table tt);

How to display multiple rows for one id in MySql? [duplicate]

SELECT DISTINCT field1, field2, field3, ......
FROM table;
I am trying to accomplish the following SQL statement, but I want it to return all columns.
Is this possible?
Something like this:
SELECT DISTINCT field1, *
FROM table;
You're looking for a group by:
select *
from table
group by field1
Which can occasionally be written with a distinct on statement:
select distinct on field1 *
from table
On most platforms, however, neither of the above will work because the behavior on the other columns is unspecified. (The first works in MySQL, if that's what you're using.)
You could fetch the distinct fields and stick to picking a single arbitrary row each time.
On some platforms (e.g. PostgreSQL, Oracle, T-SQL) this can be done directly using window functions:
select *
from (
select *,
row_number() over (partition by field1 order by field2) as row_number
from table
) as rows
where row_number = 1
On others (MySQL, SQLite), you'll need to write subqueries that will make you join the entire table with itself (example), so not recommended.
From the phrasing of your question, I understand that you want to select the distinct values for a given field and for each such value to have all the other column values in the same row listed. Most DBMSs will not allow this with neither DISTINCT nor GROUP BY, because the result is not determined.
Think of it like this: if your field1 occurs more than once, what value of field2 will be listed (given that you have the same value for field1 in two rows but two distinct values of field2 in those two rows).
You can however use aggregate functions (explicitely for every field that you want to be shown) and using a GROUP BY instead of DISTINCT:
SELECT field1, MAX(field2), COUNT(field3), SUM(field4), ....
FROM table GROUP BY field1
If I understood your problem correctly, it's similar to one I just had. You want to be able limit the usability of DISTINCT to a specified field, rather than applying it to all the data.
If you use GROUP BY without an aggregate function, which ever field you GROUP BY will be your DISTINCT filed.
If you make your query:
SELECT * from table GROUP BY field1;
It will show all your results based on a single instance of field1.
For example, if you have a table with name, address and city. A single person has multiple addresses recorded, but you just want a single address for the person, you can query as follows:
SELECT * FROM persons GROUP BY name;
The result will be that only one instance of that name will appear with its address, and the other one will be omitted from the resulting table. Caution: if your fileds have atomic values such as firstName, lastName you want to group by both.
SELECT * FROM persons GROUP BY lastName, firstName;
because if two people have the same last name and you only group by lastName, one of those persons will be omitted from the results. You need to keep those things into consideration. Hope this helps.
That's a really good question. I have read some useful answers here already, but probably I can add a more precise explanation.
Reducing the number of query results with a GROUP BY statement is easy as long as you don't query additional information. Let's assume you got the following table 'locations'.
--country-- --city--
France Lyon
Poland Krakow
France Paris
France Marseille
Italy Milano
Now the query
SELECT country FROM locations
GROUP BY country
will result in:
--country--
France
Poland
Italy
However, the following query
SELECT country, city FROM locations
GROUP BY country
...throws an error in MS SQL, because how could your computer know which of the three French cities "Lyon", "Paris" or "Marseille" you want to read in the field to the right of "France"?
In order to correct the second query, you must add this information. One way to do this is to use the functions MAX() or MIN(), selecting the biggest or smallest value among all candidates. MAX() and MIN() are not only applicable to numeric values, but also compare the alphabetical order of string values.
SELECT country, MAX(city) FROM locations
GROUP BY country
will result in:
--country-- --city--
France Paris
Poland Krakow
Italy Milano
or:
SELECT country, MIN(city) FROM locations
GROUP BY country
will result in:
--country-- --city--
France Lyon
Poland Krakow
Italy Milano
These functions are a good solution as long as you are fine with selecting your value from the either ends of the alphabetical (or numeric) order. But what if this is not the case? Let us assume that you need a value with a certain characteristic, e.g. starting with the letter 'M'. Now things get complicated.
The only solution I could find so far is to put your whole query into a subquery, and to construct the additional column outside of it by hands:
SELECT
countrylist.*,
(SELECT TOP 1 city
FROM locations
WHERE
country = countrylist.country
AND city like 'M%'
)
FROM
(SELECT country FROM locations
GROUP BY country) countrylist
will result in:
--country-- --city--
France Marseille
Poland NULL
Italy Milano
SELECT c2.field1 ,
field2
FROM (SELECT DISTINCT
field1
FROM dbo.TABLE AS C
) AS c1
JOIN dbo.TABLE AS c2 ON c1.field1 = c2.field1
Great question #aryaxt -- you can tell it was a great question because you asked it 5 years ago and I stumbled upon it today trying to find the answer!
I just tried to edit the accepted answer to include this, but in case my edit does not make it in:
If your table was not that large, and assuming your primary key was an auto-incrementing integer you could do something like this:
SELECT
table.*
FROM table
--be able to take out dupes later
LEFT JOIN (
SELECT field, MAX(id) as id
FROM table
GROUP BY field
) as noDupes on noDupes.id = table.id
WHERE
//this will result in only the last instance being seen
noDupes.id is not NULL
Try
SELECT table.* FROM table
WHERE otherField = 'otherValue'
GROUP BY table.fieldWantedToBeDistinct
limit x
You can do it with a WITH clause.
For example:
WITH c AS (SELECT DISTINCT a, b, c FROM tableName)
SELECT * FROM tableName r, c WHERE c.rowid=r.rowid AND c.a=r.a AND c.b=r.b AND c.c=r.c
This also allows you to select only the rows selected in the WITH clauses query.
For SQL Server you can use the dense_rank and additional windowing functions to get all rows AND columns with duplicated values on specified columns. Here is an example...
with t as (
select col1 = 'a', col2 = 'b', col3 = 'c', other = 'r1' union all
select col1 = 'c', col2 = 'b', col3 = 'a', other = 'r2' union all
select col1 = 'a', col2 = 'b', col3 = 'c', other = 'r3' union all
select col1 = 'a', col2 = 'b', col3 = 'c', other = 'r4' union all
select col1 = 'c', col2 = 'b', col3 = 'a', other = 'r5' union all
select col1 = 'a', col2 = 'a', col3 = 'a', other = 'r6'
), tdr as (
select
*,
total_dr_rows = count(*) over(partition by dr)
from (
select
*,
dr = dense_rank() over(order by col1, col2, col3),
dr_rn = row_number() over(partition by col1, col2, col3 order by other)
from
t
) x
)
select * from tdr where total_dr_rows > 1
This is taking a row count for each distinct combination of col1, col2, and col3.
select min(table.id), table.column1
from table
group by table.column1
SELECT *
FROM tblname
GROUP BY duplicate_values
ORDER BY ex.VISITED_ON DESC
LIMIT 0 , 30
in ORDER BY i have just put example here, you can also add ID field in this
Found this elsewhere here but this is a simple solution that works:
WITH cte AS /* Declaring a new table named 'cte' to be a clone of your table */
(SELECT *, ROW_NUMBER() OVER (PARTITION BY id ORDER BY val1 DESC) AS rn
FROM MyTable /* Selecting only unique values based on the "id" field */
)
SELECT * /* Here you can specify several columns to retrieve */
FROM cte
WHERE rn = 1
In this way can get 2 unique column with 1 query only
select Distinct col1,col2 from '{path}' group by col1,col2
you can increase your columns if need
Add GROUP BY to field you want to check for duplicates
your query may look like
SELECT field1, field2, field3, ...... FROM table GROUP BY field1
field1 will be checked to exclude duplicate records
or you may query like
SELECT * FROM table GROUP BY field1
duplicate records of field1 are excluded from SELECT
Just include all of your fields in the GROUP BY clause.
It can be done by inner query
$query = "SELECT *
FROM (SELECT field
FROM table
ORDER BY id DESC) as rows
GROUP BY field";
SELECT * from table where field in (SELECT distinct field from table)
SELECT DISTINCT FIELD1, FIELD2, FIELD3 FROM TABLE1 works if the values of all three columns are unique in the table.
If, for example, you have multiple identical values for first name, but the last name and other information in the selected columns is different, the record will be included in the result set.
I would suggest using
SELECT * from table where field1 in
(
select distinct field1 from table
)
this way if you have the same value in field1 across multiple rows, all the records will be returned.

How to get all used values in columns in mysql?

I have a MySQL table with columns and values like this:
Column "A": 1, 5, 3, 2, 3, 1, 4, 5, ...
Column "B": 11, 15, 10, 12, 13, 14, 15, 10, 11, ...
Column "C": .... etc.
There is multiple columns in table with repeating numeric values.
I want to find out unique values in each column. So for column "A" it would return 1,2,3,4,5.
Currently I am using this query for one column:
SELECT concat(A) FROM table GROUP BY A;
But I don't know how to do it for multiple columns
If it's a small enough set of values, you can use the GROUP_CONCAT aggregate function, with the DISTINCT keyword
For example:
SELECT GROUP_CONCAT(DISTINCT a ORDER BY a) AS a_values
, GROUP_CONCAT(DISTINCT b ORDER BY b) AS b_values
, GROUP_CONCAT(DISTINCT c ORDER BY c) AS c_values
FROM mytable
The length of the string returned by GROUP_CONCAT is limited by the max_group_concat_len variable (it's in the reference) and I think the max_allowed_packet also comes into play.
Compare the length of the string returned to max_group_concat_len to see if it's shorter, to know that the return string hasn't been silently truncated.
If you want to combine all of those values together, into a single distinct list, you could do something like this:
SELECT GROUP_CONCAT(DISTINCT val ORDER BY val) AS col_values
FROM ( SELECT a AS val FROM mytable
UNION
SELECT b FROM mytable
UNION
SELECT c FROM mytable
) v
EDIT
I was confused by the use of the CONCAT function in the query, and misread the specification. The queries above return a single row, and returns a result that looks EXACTLY like what OP specified:
1,2,3,4,5,...
If we want to return each value on a separate row, a result that looks like this:
val
---
1
2
3
4
5
Then the query from Tim3880's answer does that, but the outer query isn't really necessary.
I'd want to add an ORDER BY, and actually write the query like this:
(SELECT a AS val FROM mytable)
UNION
(SELECT b AS val FROM mytable)
UNION
(SELECT c AS val FROM mytable)
ORDER BY 1
EDIT
Added SQL Fiddle showing how I interpret the specification (table, columns, exemplar values), and results from SQL statements above... one statement returning distinct values as individual rows (query immediately above), and a statement returning a comma separated list (the first query in my answer.)
SQL Fiddle Example HERE http://sqlfiddle.com/#!9/3d61c/1
If we want to identify which column(s) a value appears in
SELECT v.val
, GROUP_CONCAT(DISTINCT v.col ORDER BY v.col) AS in_cols
, MAX(v.col='a') AS in_col_a
, MAX(v.col='b') AS in_col_b
, MAX(v.col='c') AS in_col_c
FROM (
SELECT a AS val, 'a' AS col FROM mytable
UNION
SELECT b AS val, 'b' AS col FROM mytable
UNION
SELECT c AS val, 'c' AS col FROM mytable
) v
GROUP BY v.val
ORDER BY v.val
If your query works for A, then you can do it for A, B, C using this:
SELECT A FROM
(
SELECT A FROM table
UNION
SELECT B FROM table
UNION
SELECT C FROM table
) e
as long as the three columns have compatible types.

Comparing 3 fields in the same record in Access

I have 3 fields that I am trying to compare the values for in Access. Would be easy if there was a SMALL and LARGE function, but there is not. I need to derive the small and large values to run analysis off it. Any help appreciated.
Thanks
Here's one approach. Given a table named Table with columns ID, A, B and C:
select ID, max(Value) as MaxValue, min(Value) as MinValue
from (
select ID, A as Value from Table
union all select ID, B as Value from Table
union all select ID, C as Value from Table
) Data
group by ID

mysql select rows with same ids and preserve their order?

just a quick question:
i have to have one single query that has multiple rows - some rows are identicle - and the order of rows must be preserved in the result -
some idea of what im refering to:
SELECT id,date
FROM items
WHERE id IN (1,2,1,3)
ORDER BY id=1 DESC,id=2 DESC,id=1 DESC,id=3 DESC;
unfortunately mysql result is this:
1,2,3
not 1,2,1,3
it removes the duplicate which i have to have in my result to display in multiple panels on the same webpage -
i really dont want to loop thru each id one by one to get them the way i want to display -
is there a way to actually have one single query that will preserve the order and pull out rows based on request whether its unique or not -
Your query as it stands will never work, because duplicate values in a list of values of an IN clause are ignored. The only way to make this work is by using UNION ALL:
SELECT id, date FROM items where id = 1
UNION ALL
SELECT id, date FROM items where id = 2
UNION ALL
SELECT id, date FROM items where id = 1
UNION ALL
SELECT id, date FROM items where id = 3;
But to be frank, I suspect your data model so far past screwed it's unusable.
try
SELECT
id,
date
FROM items
WHERE id IN (1,2,1,3)
ORDER BY FIND_IN_SET(id, '1,2,1,3')
Another scrupulous way to answer a suspicious question:
SELECT
items.id,
items.date
FROM
items
JOIN
( SELECT 1 AS id, 1 AS ordering
UNION ALL
SELECT 2, 2
UNION ALL
SELECT 1, 3
UNION ALL
SELECT 3, 4
) AS auxilary
ON
auxilary.id = items.id
ORDER BY
auxilary.ordering
Another approach (untested, but should give you the idea):
CREATE TEMPORARY TABLE tt (id INT, ai int unsigned auto_increment primary key);
INSERT INTO tt (id) VALUES (1), (2), (1), (3);
SELECT
id,
date
FROM items JOIN tt USING (id)
ORDER BY tt.ai;
keeps the given order.
If you want to include the records with id=1 and the order doesn't matter as long as you get them, you can split your query into two queries, one for (1,2,3) union all the other query for id=1 or just do:
... In (1,2)
Union all
... In (1,3)
Example:
Select * from
(Select case id when 1 then 1 when 2 then 2 as pseudocol, othercolumns
From table where Id in (1,2)
Union all
Select case id when 1 then 3 when 3 then 4 as pseudocol, othercolumns
From table where Id in (1,3)) t order by pseudocol
Instead of doing what you are trying to, just select the unique rows you need. In the frontend code, store each unique row once in a key=>value structure, where key is the item ID and value is whatever data you need about that item.
Once you have that you can use frontend logic to output them in the desired order including duplicates. This will reduce the amount of redundant data you are trying to select.
For example This is not usable code - exact syntax required depends on your scripting language
-- setup a display order
displayOrder= [1,2,1,3];
-- select data from database, order doesn't matter here
SELECT id,date
FROM items
WHERE id IN (displayOrder);
-- cache the results in a key=> value array
arrCachedRows = {};
for (.... each db row returned ...) {
arrCachedRows[id] = date;
}
-- Now output in desired order
for (listIndex in displayOrder) {
-- Make sure the index is cached
if (listIndex exists in arrCachedRow) {
echo arrCachedRows[listIndex ];
}
}
If you must persist in using UNION despite my warnings
If you go against the above recommendation and absolutely MUST have them back in 1 query in that order then add on an additional row which will enforce the row order. See below query where I use variable #subIndex to add an incrementing value as subIndex. This in turn lets you reorder by that and it'll be in the requested order.
SELECT
i.*
FROM (
SELECT #subIndex:=#subIndex+1 AS subIndex, id, date FROM items where id = 1
UNION
SELECT #subIndex:=#subIndex+1 AS subIndex, id, date FROM items where id = 2
UNION
SELECT #subIndex:=#subIndex+1 AS subIndex, id, date FROM items where id = 1
UNION
SELECT #subIndex:=#subIndex+1 AS subIndex, id, date FROM items where id = 3
) AS i,(SELECT #subIndex:=0) v
ORDER BY i.subIndex
Or a slightly cleaner version that keeps item selection until the outside and hides the subindex
SELECT
items.*
FROM items
-- initialise variable
INNER JOIN (SELECT #subIndex:=0) v
-- create a meta-table with the ids desired in the order desired
INNER JOIN (
SELECT #subIndex:=#subIndex+1 AS subIndex, 1 AS id
UNION
SELECT #subIndex:=#subIndex+1 AS subIndex, 2 AS id
UNION
SELECT #subIndex:=#subIndex+1 AS subIndex, 1 AS id
UNION
SELECT #subIndex:=#subIndex+1 AS subIndex, 3 AS id
) AS i
ON i.id = items.id
-- order by the subindex from i
ORDER BY i.`subIndex` ASC