MySQL - One SQL request instead two (both request include diffrent COUNT(*) - mysql

Once again i need yours help ;). I have a lot data and mysql request are slower and slower so the need request that i need i want group in one comand.
My example DB structure:
|product|opinion (pos/neg)|reason|
__________________________________
|milk | pos | good |
|milk | pos |fresh |
|chocolate| neg | old |
|milk | neg | from cow|
So i need information about all diffrent product (GROUP BY) count of it, and count of pos opinion for each product. I want output like that:
|product|count|pos count|
_________________________
|milk | 3 | 2 |
|chocolate| 1 | 0 |
I hope that my explain was good enought ;)
Or go to work: I write two commands
SELECT COUNT(*) as pos count FROM table WHERE product = "milk" AND opinion = "pos" GROUP BY `opinion`
And Second one
SELECT product, COUNT(*) FROM table GROUP BY `product`
I don't know how to join this two request, maybe that is impossible? In my real use code i have additional category collumn and i use WHERE in second command too

select product,
count(*) as total_count
sum(
case when opinion='pos' then 1
else 0 end
) as pos_count
from the_table
group by product;

SELECT product,
COUNT(*) TotalCount,
SUM(opinion = 'pos') POSCount
FROM tableName
GROUP BY product
SUM(opinion = 'pos') is MySQL specific syntax that counts the total of result based from the result of boolean arithmethic. If you want it to be more RDBMS friends, use CASE
SUM(CASE WHEN opinion = 'pos' THEN 1 ELSE 0 END)
SQLFiddle Demo

Related

How Do I convert this table from row to column?

I have a table that contains id and country name, and I need to convert them so the id with more than 1 country will display in 1 row.I have been searching in this forum for over an hour and found nothing.
I tried if using the pivot function can help me to achieve the result i wanted, but I feel like using pivot does not work on my case here.
This is a mini version of the table I have. The number of distinct value in the field "country" will be over 100, so I can just say something like when county = '..' as this will be to repetitive.
enter code here
+----+--------+
| id | country|
+----+--------+
| 1 | US |
| 1 | UK |
| 2 | JP |
+----+--------+
Desired outcome I am looking for:
enter code here
+----+-----------+-----------+
| id | country_1 | country_2 |
+----+-----------+-----------+
| 1 | US | UK |
| 2 | JP | null |
+----+-----------+-----------+
I found this question which is similar but it is the opposite of what I am trying to achieve.
MySQL statement to pivot table without using pivot function or a union
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
update:
Thank you so much for all of your helps. I may not have used the queries of yours to solve my problem - as of the fact that the syntax is a bit diff running in snowflake. However, I got the insights I need from all of you.
here is my solution:
enter code here
select t1.id,
max(iff(t1.row_number = 1, t1.country ,null)) as country_1,
max(iff(t1.row_number = 2, t1.country ,null)) as country_2,
max(iff(t1.row_number = 3, t1.country, null)) as country_3
from
(
select id, country, row_number() over (partition by id order by id ) as
row_number
from table
) t1
group by t1.id
Whereas you could do it with "pivoting", what will happen when you have 3 countries? Or 4? Or 17?
May I suggest this:
SELECT id,
GROUP_CONCAT(country)
FROM tbl
GROUP BY id;
You will get something like:
1 US,UK
2 JP
use aggregation
select id, max(case when id=1 then country end ) as country_1,
max(case when id=2 then country end ) as country_2
from tbale group by id
As you comment on #Rick answer you have max 3 country for each id then you can use this
select
id,
(select country from test where test.id=t.id limit 0,1)as country_1,
(select country from test where test.id=t.id limit 1,1)as country_2,
(select country from test where test.id=t.id limit 2,1)as country_3
from test as t
group by id;
DEMO
You can try this following script with RowNumber generated per id. As you confirmed there are maximum 3 country per id, we can easily generate your desired result set by handling RowNumber 1,2 & 3
SELECT ID,
MAX(CASE WHEN RowNumber = 1 THEN country ELSE NULL END) Country_1,
MAX(CASE WHEN RowNumber = 2 THEN country ELSE NULL END) Country_2,
MAX(CASE WHEN RowNumber = 3 THEN country ELSE NULL END) Country_3
FROM
(
SELECT id,
country,
#row_num :=IF(#prev_value = concat_ws('',id),#row_num+1,1)AS RowNumber
,#prev_value := concat_ws('',id)
FROM tbale
ORDER BY id
)A
GROUP BY id
There's no "dynamic" PIVOT in SQL. You need to specify the list of columns when writing the query. Your options are:
If you know the number of columns in advance, then #ZaynulAbadinTuhin solution is the easier. It seems, however, this is not your case.
If you don't know the number of columns in advance and you want them all concatenated in a single column, then #Rick James solution is the best.
Otherwise, you can still use some kind of dynamic SQL in your app or in a stored procedure that will build the SQL query at runtime, based on the existing values of the table. But this solution would require much more programming. It's not a single/simple SQL query anymore. See Rick James's Pivoting in MySQL stored procedure.

SQL query to pivot table on column value

This has probably been covered before, but my skill level with SQL is low enough that I'm not even sure how to properly search for what I want!
I think what I want to do is relatively simple, and it seems that using pivoting might be the solution, but I'm really not sure about the syntax for that.
I have a SELECT query which can return data in this format:
TeamID | PlayerName
--------+-------------------
1 | Arthur Schiller
1 | Dimitre Rogatchev
1 | Mohamed Hechmeh
1 | Santosh Pradhan
2 | Adriano Ferrari
2 | Amanda Compagnone
2 | Danail Delchev
2 | David Bergin
I want to create columns from the ID with rows filled with the appropriate names, like this:
1 | 2
------------------+-------------------
Arthur Schiller | Adriano Ferrari
Dimitre Rogatchev| Amanda Compagnone
Mohamed Hechmeh | Danail Delchev
Santosh Pradhan | David Bergin
The purpose is to use the returned data in a php mysqli_fetch_assoc call to display the two columns on a website.
Thanks in advance for any help!
Unfortunately MySQL doesn't support windowing functions to generate a unique value for each of those rows per team. You can create a derived "row number" using variables or you could use a correlated subquery similar to:
select
max(case when teamid = 1 then playername else '' end) Team1,
max(case when teamid = 2 then playername else '' end) Team2
from
(
select TeamId,
PlayerName,
(select count(*)
from yourtable d
where t.teamId = d.TeamId
and t.playername <= d.PlayerName) rn
from yourtable t
) d
group by rn;
See SQL Fiddle with Demo.
Note: depending on the size of your data you might have some performance issues. This works great with smaller datasets.
you cannot do this with a pivot because there is no common ground to pivot off of.. what you can do is make a row count that you join on for the second team.. try this
SELECT t.playername as '1', f.playername as '2'
FROM
( SELECT #a := #a + 1 as id, playername
FROM players
WHERE teamid = 1
)t
LEFT JOIN
( SELECT #b := #b +1 as id , playername
FROM players
WHERE teamid = 2
)f ON f.id = t.id
CROSS JOIN (SELECT #a :=0, #b :=0)t1
DEMO
This will be much easier to do in the PHP code rather than trying to do it in the SQL. Just perform the query and then loop through the results adding them to an array for each team.
In general SQL is good for storing, associating, indexing and querying.
Massaging it into the right format you need to display is usually easier, cleaner and neater to do in code though.

SQL - SELECT Merge rows if multiple results are found

I have an SQL table setup similar to this:
name | subject |
-------+----------+
Harry | Painting |
Sandra | Soccer |
Sandra | English |
How can I write a select statement that merges the rows if they have multiple subject, so it would output a result like this:
name | subject 1 | subject 2 |
-------+------------+------------+
Harry | Painting | |
Sandra | Soccer | English |
You shouldn't. The best approach is to join the tables so you have:
Harry, Painting
Sandra, Soccer
Sandra, English
And then process these rows in your scripting language (PHP etc) to turn it into the hierarchical data structure you desire
In your example above, what would happen when there's 3 subjects per person, 10, 100, etc.
SQL only really works with two dimensional data. For three dimensions you either need to pre/post process as i've suggested, or move to something like NoSQL mongoDB that deals with structured objects instead of table rows.
Since you mentioned that the maximum number of subjects is only 2, you can therefore, generate a sequential number for each name and used that to pivot the columns.
SELECT Name,
MAX(CASE WHEN rn = 1 THEN Subject END) Subject1,
MAX(CASE WHEN rn = 2 THEN Subject END) Subject2
FROM
(
SELECT A.name,
A.subject,
(
SELECT COUNT(*)
FROM tableName c
WHERE c.name = a.name AND
c.subject <= a.subject) AS rn
FROM TableName a
) aa
GROUP BY Name
SQLFiddle Demo
The above is an SQL way.
You need a PIVOT routine. Serach for this in the engine of your choice.
Some RDBMSs have this built in. There is an alternative using a CASE statement in the SELECT clause, for which there are many blog posts out there.
You can accomplish this in one statement using a combination of substring_index() and group_concat() like this (SQLfiddle)
SELECT DISTINCT s.name,
substring_index(p.subject_list, ',', 1) AS "subject_1",
IF(instr(p.subject_list, ','),
substring_index(p.subject_list, ',', -1),
NULL
) AS "subject_2"
FROM subjects s
JOIN (SELECT name, GROUP_CONCAT(subject) AS "subject_list"
FROM subjects
GROUP BY name
) p on p.name = s.name
;

Counting partial values or conditional values

I hope someone can lend an assist and some advise here. I'm trying to get a fairly complex result and not sure if I can do it as one query with subqueries, a union, or simply separate queries to be merged into excel after the fact.
I'm working with a legacy database from my predecessor with the following tables:
Business (columns working with: id, sector, state)
Forms (columns working with: Submitted (Y/N), id, business_id)
Inventory (Columns working with: In_stock (Y/N), id, form_id)
I'm trying to get a final result that looks like this:
| SubmittedForms | Unsubmitted Forms | Sector | State |
|-----------------------------------------------------|
| 10 | 5 | Agr | UT |
| 0 | 7 | Chem | MT |
| 2 | 1 | Bio | OK |
| 13 | 0 | Chem | NM |
The main problem I'm getting is that while submitted forms doesn't need any further arguments and is a simple count, the unsubmitted forms are dependent on the Inventory.in_stock='Y'. Here's my query for the submitted forms:
SELECT COUNT(Forms.id) AS Submitted, Business.sector, Business.state
FROM Forms
JOIN Business ON Forms.business_id=Business.id
WHERE Forms.submitted='Y'
GROUP BY Business.state, Business.sector
Unfortunately, I can't seem to get the unsubmitted forms number to calculate correctly. It just returns the total count of rows where in_stock is Y for that sector.
If it's easier to run a separate query for Submitted and Unsubmitted that's fine for the end result but I need some help getting the correct count of Unsubmitted forms with the in_stock flagged as Y. Also, I attempted to use a COUNT DISTINCT but takes way too long, was still running after 10 minutes. Another complication I can envision in a single query option is the possibility of 0/null values in either Submitted or Unsubmitted forms
Any help is greatly appreciated!
One option:
SELECT COUNT(CASE WHEN Forms.submitted = 'Y' THEN 1 END) SubmittedForms,
COUNT
( CASE WHEN Forms.submitted = 'N'
AND EXISTS ( SELECT 1
FROM Inventory
WHERE form_id = Forms.id
AND in_stock = 'Y'
)
THEN 1
END
) UnsubmittedForms,
Business.sector Sector,
Business.state State
FROM Forms
RIGHT
OUTER
JOIN Business
ON Forms.business_id = Business.id
GROUP
BY Business.sector,
Business.state
;
Another option, which might perform better:
SELECT COUNT(CASE WHEN Forms.submitted = 'Y' THEN 1 END) SubmittedForms,
COUNT(CASE WHEN Forms.submitted = 'N' THEN 1 END) UnsubmittedForms,
Business.sector Sector,
Business.state State
FROM ( SELECT *
FROM Forms
WHERE submitted = 'Y'
OR id IN ( SELECT DISTINCT form_id
FROM Inventory
AND in_stock = 'Y'
)
) Forms
RIGHT
OUTER
JOIN Business
ON Forms.business_id = Business.id
GROUP
BY Business.sector,
Business.state
;

SQL use of conditional AND on a join table to ensure at least two or multiple values are matched

I have a join table named languages_services that basically joins the table services and languages.
I need to find a service that is able to serve both ENGLISH (language_id=1) and ESPANOL (language_id=2).
table languages_services
------------------------
service_id | language_id
------------------------
1 | 1
1 | 2
1 | 3
2 | 1
2 | 3
With the data provided above, I want to test for language_id=1 AND language_id=2 where the result would look like this
QUERY RESULT
------------
service_id
------------
1
Obviously it doesn't return the one with service_id=2 because it doesn't service Espanol.
Any tips on this is greatly appreciated!
SELECT
service_id
FROM
language_services
WHERE
language_id = 1
OR language_id = 2
GROUP BY
service_id
HAVING
COUNT(*) = 2
Or...
WHERE
lanaguage_id IN (1,2)
GROUP BY
service_id
HAVING
COUNT(*) = 2
If you're always looking at 2 languages you could do it with joins, but the aggregate version is easier to adapt to differing numbers of language_ids. (Add an OR, or add an item to the IN list, and change the COUNT(*) = 2 to COUNT(*) = 3, etc, etc).
Be aware, however, that this scales very poorly. And with this table structure there isn't much you can do about that.
EDIT Example using a join for 2 languages
SELECT
lang1.service_id
FROM
language_services AS lang1
INNER JOIN
language_services AS lang2
ON lang1.service_id = lang2.service_id
WHERE
lang1.language_id = 1
AND lang2.language_id = 2