Get Count of different values in comma separated row in mysql - mysql

A table Jobs which have 2 column JobId, City when we save job a job location may be multiple city like below
-----------------------------
JobId City
-------------------------------
1 New York
2 New York , Ohio , Virginia
3 New York , Virginia
how i count jobid in perticular city like i want count of jobid in New York city
i want result like
New York 3
Ohio 1
Virginia 2

Your database is poorly designed and you are going to have a lot of trouble down the line.
Using the current structure you can get the count using the find_in_set function but that you should avoid .
Your table is as
create table test
(jobid int ,city varchar(100));
insert into test values
(1,'New York'),
(2,'New York, Ohio,Virginia'),
(3,'New York,Virginia');
Now to get the count you can use the following
select
count(*) as tot from test
where
find_in_set('Virginia',city) > 0;
As I mentioned this is a poor db design the ideal would be as
first a job table with job details
a location table containing all the locations
and finally a table linking a job and a location
So it would look like
create table jobs (jobid int, name varchar(100));
insert into jobs values
(1,'PHP'),(2,'Mysql'),(3,'Oracle');
create table locations (id int, name varchar(100));
insert into locations values (1,'New York'),(2,'Ohio'),(3,'Virginia');
create table job_locations (id int, jobid int, location_id int);
insert into job_locations values
(1,1,1),(2,2,1),(3,2,2),(4,2,3),(5,3,1),(6,3,3);
Now getting the count and many more operations will be fairly easy
select
count(j.jobid) as tot
from jobs j
join job_locations jl on jl.jobid = j.jobid
join locations l on l.id = jl.location_id
where
l.name = 'Virginia'
For counting all the jobs per city and using the above schema it would very simple
select
l.name,
count(j.jobid) as tot
from jobs j
join job_locations jl on jl.jobid = j.jobid
join locations l on l.id = jl.location_id
group by l.name
DEMO

SELECT
SUBSTRING_INDEX(SUBSTRING_INDEX(all_city, ',', num), ',', -1) AS one_city,
COUNT(*) AS cnt
FROM (
SELECT
GROUP_CONCAT(city separator ',') AS all_city,
LENGTH(GROUP_CONCAT(citySEPARATOR ',')) - LENGTH(REPLACE(GROUP_CONCAT(citySEPARATOR ','), ',', '')) + 1 AS count_city
FROM table_name
) t
JOIN numbers n
ON n.num <= t.count_city
GROUP BY one_city
ORDER BY cnt DESC;
for getting count of comma separated distinct value run above query but getting correct resulr you should use one more table **numbers** which have only one column num integer type and insert some values.
if you getting error during GROUP_CONCAT(city separator ',') AS all_city in this condition set a global variable " SET group_concat_max_len = 18446744073709547520; "

SELECT COUNT(*) AS jobs
FROM Jobs
WHERE FIELD_IN_SET('New York') > 0
;
You should read about database normalization though. Having a comma separated list of values in a database table always has a 'smell', e.g. you can only check for a specific city name here and can't easily create a list of job counts for all cities referred to in the job table in one go ...
See e.g. http://en.wikipedia.org/wiki/Database_normalization for a start ...

Make JobId and City column as joined primary key. This will make your life easier. Do not insert multiple cities separated by commas.
-------------------------------------------------
JobId City // Other columns
-------------------------------------------------
1 New York
2 New York
2 Ohio
2 Virginia
3 New York
3 Virginia
Now you make the query will be something like this
select city, count(jobid) FROM TABLE GROUP BY city // Result will be New York 3, Ohio 1 and Virginia 2

Design your table as mentioned below. Each city name and Job Id in each row.
Job ID City Name
1 New York
2 New York
1 Seattle
Then use the query as mentioned in the below link.
SQL: How to get the count of each distinct value in a column?

Try this query,
SELECT COUNT(jobid), city FROM TABLE where city like '%newyork%';

Related

MySQL: How to use partial matches across rows to set the final value to NULL?

I have a table with partial duplicate rows and I want to use the partial matches across the rows to set a value to NULL.
In this particular case, if the 'Name', 'Age', and 'Profession' columns are the same and the 'School' column is different, I want to set the 'School' column to NULL.
This is what my table looks like now:
Name Age Profession Salary School
John 21 Teacher 50,000 A
Lisa 24 Engineer 75,000 B
John 21 Teacher 55,000 C
This is what I want it to look like:
Name Age Profession Salary School
John 21 Teacher 50,000 NULL
Lisa 24 Engineer 75,000 B
John 21 Teacher 55,000 NULL
How can I create a query that does this? Thanks!
Ideally there should be some kind of primary key in your table. That lacking, we can try the following update join approach:
UPDATE yourTable t1
INNER JOIN
(
SELECT Name, Age, Profession
FROM yourTable
GROUP BY Name, Age, Profession
HAVING COUNT(*) > 1
) t2
ON t2.Name = t1.Name AND
t2.Age = t1.Age AND
t2.Profession = t1.Profession
SET
School = NULL;
SELECT Name, Age, Profession, Salary,
CASE WHEN COUNT(School) OVER (PARTITION BY Name, Age, Profession) = 1
THEN School
END AS School
FROM sourcetable

How To Remove a Second Id From JOIN In SQL

I'm new to programming in SQL, and I need help on removing the second 1 through 5 id's on the right from my output (the numbers are highlighted in bold):
ID
Name
Gender
ID
Country
1
Abdur-Rahman
M
1
America
2
Don Madden
M
2
England
3
Dustin Tompkins
M
3
America
4
Nicki Harris
F
4
Germany
5
Samantha Harris
F
5
France
CREATE TABLE test ( id INTEGER PRIMARY KEY, name TEXT);
INSERT INTO test VALUES (1,"Albert Franco");
INSERT INTO test VALUES (2,"Don Madden");
INSERT INTO test VALUES (3,"Dustin Tompkins");
INSERT INTO test VALUES (4,"Nicki Harris");
INSERT INTO test VALUES (5,"Samantha Harris");
ALTER TABLE test
ADD COLUMN gender TEXT;
UPDATE test
SET gender = 'M'
WHERE id = 1;
UPDATE test
SET gender = 'M'
WHERE id = 2;
UPDATE test
SET gender = 'M'
WHERE id = 3;
UPDATE test
SET gender = 'F'
WHERE id = 4;
UPDATE test
SET gender = 'F'
WHERE id = 5;
CREATE TABLE country (
id INTEGER,
nation TEXT
);
INSERT INTO country VALUES (1,"America");
INSERT INTO country VALUES (2,"England");
INSERT INTO country VALUES (3,"America");
INSERT INTO country VALUES (4,"Germany");
INSERT INTO country VALUES (5,"France");
SELECT * FROM test
JOIN country
ON test.id = country.id;
To actually answer your question, you should explicitly state the columns you want, i.e.
SELECT t.id, t.name, t.gender, c.nation
FROM test AS t
JOIN country AS c
ON c.id = t.id;
It is however worth noting that your schema doesn't really make sense, you have to duplicate countries, which is not normalised. You've simply created a 1:1 relationship, you'd be as well just adding a nation column to test.
A better solution though would be to normalise the data, so your country table would become:
CREATE TABLE Country
(
Id INT AUTO_INCREMENT PRIMARY KEY,
Nation VARCHAR(50)
);
INSERT INTO Country(Nation)
VALUES ('America'), ('England'), ('France'), ('Germany');
Then in your Test Table, add CountryId as foreign key to your country table:
CREATE TABLE Test
(
Id INT AUTO_INCREMENT PRIMARY KEY,
Name VARCHAR(500) NOT NULL,
Gender CHAR(1) NOT NULL,
CountryId INT NOT NULL,
CONSTRAINT FK_Test__CountryId FOREIGN KEY (CountryID) REFERENCES Country (Id)
);
INSERT INTO Test (Name, Gender, CountryId)
SELECT data.Name, data.Gender, c.Id
FROM (
SELECT 'Albert Franco' AS Name, 'M' AS Gender, 'America' AS Nation
UNION ALL
SELECT 'Don Madden' AS Name, 'M' AS Gender, 'England' AS Nation
UNION ALL
SELECT 'Dustin Tompkins' AS Name, 'M' AS Gender, 'America' AS Nation
UNION ALL
SELECT 'Nicki Harris' AS Name, 'F' AS Gender, 'Germany' AS Nation
UNION ALL
SELECT 'Samantha Harris' AS Name, 'F' AS Gender, 'France' AS Nation
) AS data
INNER JOIN Country AS c
ON c.Nation = data.Nation;
Your final query is largely similar:
SELECT t.Id, t.Name, t.Gender, c.Nation
FROM Test AS t
INNER JOIN Country AS c
ON c.Id = t.CountryId;
But you have now normalised your countries, so America only appears once. Obviously in a simple example this only saves you one row, but if you have a lot of names, this has saved a lot of duplication, and potential for error. With free type entry (e.g. having a nation column in test) you inevitably end up with multiple variations of everything, e.g. "America", "USA", "U.S.A", "US", "U.S", "United States", this doesn't even consider typos! All of this leads to headaches down the road.
Full Example on SQL Fiddle
*N.B. There's a pretty good argument that the country table should not have a surrogate primary key (AUTO_INCREMENT) and it should instead use the ISO country code. The natural vs surrogate keys debate has been going on for years and years, and is well beyond the scope of this answer
If I understood your question correctly, you want something like this
Then you need to write your query like this
Select test.*, country.nation from test join country on test.id=country.id;
Now, you will only receive the id from your test table

Get the latest record by date with the same object name

How can I get courseName = 'Music' with the latest date?
courseName | dateOfEnrollment
----------------|-----------------------
Music | 2016-07-24
Art | 2016-07-01
Art | 2016-07-23
Music | 2016-07-25
As I tried with the statement below
SELECT courseName, dateOfEnrollment FROM MyDatabase.dbo.courseEnrollment WHERE courseName = 'Music' AND GETDATE() >= MAX(dateOfEnrollment)
it returns error:
An aggregate may not appear in the WHERE clause unless it is in a subquery contained in a HAVING clause or a select list, and the column being aggregated is an outer reference.
My ultimate goal is to put that statement in an IF statement, if it is not null, then I will insert new data into the table.
Update (My answer):
This is working for me:
IF EXISTS (SELECT TOP 1 courseName, dateOfEnrollment FROM courseEnrollment WHERE courseName = 'Music' ORDER BY dateOfEnrollment DESC)
*Insert data into the table*
ELSE
*No need to insert*
SELECT
courseName,
dateOfEnrollment
FROM
MyDatabase.dbo.courseEnrollment
WHERE
courseName = 'Music'
ORDER BY
dateOfEnrollment
LIMIT 1
That's how your query should look like, but assuming your main goal - you only need to check is there any 'Music' row, you should not be interested in dateOfEnrollment column at all. Am i right or missed something?
So your final query (to insert a row depending on a condition) will be:
INSERT INTO courseEnrollment
SELECT 'Music', CURDATE() FROM courseEnrollment
WHERE courseName = 'Music';
You can give it a try:
SELECT
CE.*
FROM courseEnrollment CE
INNER JOIN
(
SELECT
courseName,
MAX(dateOfEnrollment) max_enrollment_date
FROM courseEnrollment
WHERE courseName ='Music'
) t
ON CE.courseName = t.courseName AND CE.dateOfEnrollment = t.max_enrollment_date;
Note: The output of this query might contain more than one entries if multiple entries under Music course have the latest date.
TEST:
CREATE TABLE courseEnrollment (
courseName VARCHAR (100),
dateOfEnrollment date
);
INSERT INTO courseEnrollment (
courseName,
dateOfEnrollment
)
VALUES
('Music', '2016-07-24'),
('Art', '2016-07-01'),
('Art', '2016-07-23'),
('Music', '2016-07-25');
Running the above query on these given data you will get an output like below:
Output:
courseName dateOfEnrollment
Music 2016-07-25
EDIT:
WORKING DEMO

Get duplicate rows in different table mysql

I have a table named 'Student' with columns 'Name' and 'Roll Number'
name Roll_Number
A 1
A 2
A 1
B 2
B 2
C 3
C 2
D 4
I want to delete all the rows from this table having same name but different Roll_number and insert those rows in new table 'Temp'. So after operation Both tables should be like this
Table Student
name Roll_Number
B 2
B 2
D 4
Table Temp
name Roll_Number
A 1
A 2
A 1
C 3
C 2
Because A and C, both have different values for Roll_Number column, So we delete all the entries of A and C from Student table and add it to Temp Table
So how this can be done through mysql query?
Try something like:
For Student table:
SELECT name, Roll_Number
FROM Student
GROUP BY name
HAVING COUNT(DISTINCT Roll_Number) = 1
For Temp table:
SELECT name, Roll_Number
FROM Student
GROUP BY name
HAVING COUNT(DISTINCT Roll_Number) > 1
Here is the query that work for me, created from SMA and Tim answers.
For inserting into Temp Table
INSERT INTO Temp (name, Roll_Number)
SELECT name, Roll_Number
FROM Student
WHERE name IN (SELECT name
FROM Student
GROUP BY name
HAVING COUNT(DISTINCT Roll_Number) > 1);
Deleting record query is same as mentioned by Tim
DELETE FROM Student
WHERE name IN (SELECT name FROM Temp)
Thanks to both of them
Here is a query to insert all records which, for a given name have more than one Roll_Number, into a new temporary table Temp:
INSERT INTO Temp (name, Roll_Number)
SELECT name, Roll_Number
FROM Student
WHERE name IN (SELECT name
FROM Student
GROUP BY name
HAVING COUNT(DISTINCT Roll_Number) > 1)
And here is a query to delete these same records from Student:
DELETE FROM Student
WHERE name IN (SELECT name FROM Temp)
Note that I did the insertion into the new table first before deleting them from the old table (for obvious reasons).

SQL Query Two Tables at once and Loop Result as JSON objects

I'm not exactly sure what I am looking for here, so apologies if this has already been covered here, I'm not sure what I need to search for!
I have a MySQL database with a table called "Locations" which looks a bit like this
id | name | other parameters
1 | shop1 | blah
2 | shop2 | blah
etc
and a table of customer queries
id | customer | department
1 | john | shop2
2 | Joe | shop2
3 | James | shop1
4 | Sue | shop2
etc
I want to query this and return a JSON object that looks like this
{"location":"shop1","queryCount":"1"},{"location":"shop2","queryCount":"3"}
The location table can be added to with time, and obviously the customer queries will be to, so both need dynamic queries.
I tried this by getting a list of locations by a simple SELECT name from locations query, turning it into an array and then looping through that as follows:
For i = UBound(listofLocations) To 0 Step -1
locations.id = listofLocations(i)
locations.queryCount= RESULT OF: "SELECT COUNT(id) as recordCount from queries WHERE department=listofLocations(i)"
objectArray.Add(locations)
Next
This works, but it is inefficient calling the database through the loop, how do I avoid this?
Thanks
The inefficient is because you are using nested query,
you should use LEFT JOIN, and you just need single query
Here is the SQL:-
select l.name, count(*) as recordCount
from Locations as l
left join customer as c
on l.name = c.department
group by l.id;
And your schema is not very optimized.
Your schema for customer should be
id, customer, location_id <-- using name is redundant,
<-- which should represent in ID (location ID)
First, I must recommend that your "department" field be changed. If you change the spelling of a name in the locations table, your relationships break.
Instead, use the id from the location table, and set up a foreign key reference.
But that's an aside. Using your current structure, this is what I'd do in SQL...
SELECT
location.name,
COUNT(queries.id) AS count_of_queries
FROM
locations
LEFT JOIN
queries
ON queries.department = locations.name
GROUP BY
location.name
Using a LEFT JOIN ensures that you get EVERY location, even if there is no query for it.
Using COUNT(queries.id) instead of COUNT(*) gives 0 if there are no associated records in the queries table.
You can then loop through the result-set of one query, rather than looping multiple queries, and build your JSON string.
It is possible to build the string in SQL, but that's generally considered bad practice. Much better to keep you SQL about data, and you php/whatever about processing and presenting it.
Try this:
SELECT T1.name, (SELECT COUNT(*) FROM queries AS T2 WHERE T2.department = T1.name) AS number FROM locations AS T1;
I agree that your scheme is not proper, you should reference department by Id, not by name
I think you're just looking for "GROUP BY"
SELECT
department_id as location,
COUNT(id) as querycount
FROM
(table of customer queries)
GROUP BY
department_id
As for the database structure... If possible, I would change the tables a bit:
Location:
id | name | other parameters
Customer:
id | name | whatever
table_of_customer_queries:
id | customer_id | location_id
1 | 2 | 2
2 | 4 | 2
3 | 2 | 1
4 | 1 | 2
GROUP BY will only give you results for those departments that have queries. If you want all departments, the LEFT JOIN option mentioned earlier is the way to go.
First off, if your Locations table really stores Department information, rename it as such. Then, change Customer.department to be a fk reference to the Locations.id column (and renamed to department_id), not the name (so much less to get wrong). This may or may not give you a performance boost; however, keep in mind that the data in the database isn't really meant to be human readable - it's meant to be program readable.
In either case, the query can be written as so:
SELECT a.name, (SELECT COUNT(b.id)
FROM Customer as b
WHERE b.department_id = a.id) as count
FROM Location as a
If you don't change your schema, your answer is to do a group by clause that counts the number of queries to each location.
Creating tables like yours:
create table #locations (id integer, name varchar(20), other text)
create table #queries (id integer, customer varchar(20), department varchar(20))
insert into #locations (id, name, other) values (1, 'shop1', 'blah')
insert into #locations (id, name, other) values (2, 'shop2', 'blah')
insert into #queries (id, customer, department) values (1, 'john', 'shop2')
insert into #queries (id, customer, department) values (2, 'Joe', 'shop2')
insert into #queries (id, customer, department) values (3, 'James', 'shop1')
insert into #queries (id, customer, department) values (4, 'Sue', 'shop2')
Querying your data:
select
l.name as location,
count(q.id) as queryCount
from #locations as l
left join #queries as q on q.department = l.name
group by l.name
order by l.name
Results:
location queryCount
-------------------- -----------
shop1 1
shop2 3