Select primary photo if exist else pick first uploaded photo - mysql

I am trying to retrieve a single picture set by the user as the primary picture from table as below:
SELECT p.*, ph.* FROM place AS p
INNER JOIN photo as ph
ON p.place_id = ph.place_id
WHERE ph.primary_pic = 'X';
But not all user has set their primary picture, resulting in the query does not return anything.
IF(query is empty)
//perform SQL again with primary_pic = ''
Is there any ways or syntax that could be use to query this with one single SQL statement?

I was working in MS SQL but I don't see anything MS-specific except for the table variables. So if you change that to existing tables, it should run on MySQL too. (I am not sure but I guess MySQL should have EXCEPT set operation.)
-- sample data start
declare #place as table (plid int, plname nvarchar(100))
declare #photo as table (phid int, phname nvarchar(100), plid int, primary_pic nvarchar(1))
insert into #place values (1, 'aaa')
insert into #place values (2, 'bbb')
insert into #photo values (1, 'aaa_1.jpg', 1, '')
insert into #photo values (2, 'aaa_2.jpg', 1, 'X')
insert into #photo values (3, 'aaa_3.jpg', 1, '')
insert into #photo values (4, 'aaa_4.jpg', 1, '')
insert into #photo values (5, 'bbb_1.jpg', 2, '')
insert into #photo values (6, 'bbb_2.jpg', 2, '')
insert into #photo values (7, 'bbb_3.jpg', 2, '')
insert into #photo values (8, 'bbb_4.jpg', 2, '')
-- sample data end
-- note: #place and #photo are table variables in MS SQL
select p.*, ph2.*
from #place p inner join #photo ph2 on p.plid = ph2.plid
inner join (
select ph.plid, ph.primary_pic, min(ph.phid) phid
from #photo ph inner join
(select distinct plid from #photo where primary_pic <> 'X'
except
select distinct plid from #photo where primary_pic = 'X') hasnoprimary
on hasnoprimary.plid = ph.plid
group by ph.plid, ph.primary_pic
union
select ph.plid, ph.primary_pic, min(ph.phid) phid
from #photo ph inner join
(select distinct plid from #photo where primary_pic = 'X') hasprimary
on hasprimary.plid = ph.plid
where primary_pic = 'X'
group by ph.plid, ph.primary_pic
) trickypart on trickypart.phid = ph2.phid
Method: (1) get two lists of place IDs. One for which there is a primary photo and another for which there isn't (this is where I used EXCEPT). (2) join the photos table to both of them separately to get the photo IDs. For the first list, it is what was marked with X, for the second it is the minimum of all photo IDs. (3) make a union of the two. (4) join it back to places and photos.
In MS SQL it works ang gives the following for the sample data above:
plid plname phid phname plid primary_pic
----------- -------------- ----------- -------------- ----------- -----------
1 aaa 2 aaa_2.jpg 1 X
2 bbb 5 bbb_1.jpg 2

Related

How to count all rows with multiple table joins and where conditions?

I have the following tables:
create table loans
(
id int null,
status int null,
user_id int null
);
INSERT INTO loans VALUES (1, 1, 1);
INSERT INTO loans VALUES (2, 0, 1);
INSERT INTO loans VALUES (3, 1, 1);
create table deals
(
id int null,
status int null,
user_id int null
);
INSERT INTO deals VALUES (2, 0, 1);
INSERT INTO deals VALUES (3, 0, 1);
create table listings
(
id int null,
status int null,
user_id int null
);
INSERT INTO listings VALUES (1, 1, 1);
INSERT INTO listings VALUES (2, 1, 1);
INSERT INTO listings VALUES (3, 1, 1);
And have the following SQL:
SELECT COUNT(*) AS active_items
FROM loans
LEFT JOIN deals ON deals.user_id = 1
LEFT JOIN listings ON listings.user_id = 1
WHERE
loans.status = 1
AND deals.status = 1
AND listings.status = 1
AND loans.user_id = 1
The goal is to count all the rows where each table item has a status of 1, leaving out any that have a status of 0. My query which I have made seems to only return 0 all the time and I do not understand why? How can I query the database so I can find each loan, deal and listing which has a status of 1 and returns in one total called active_items? Why does my query not work?
DB Fiddle: https://www.db-fiddle.com/f/g9CoA9CdDujqzG4ZpgmJXh/1
The output for active_items is expected to be 5.
Don't use JOIN for this, since you're not relating the tables to each other. Just do 3 separate queries and add the counts.
SELECT SUM(count) AS total
FROM (
SELECT COUNT(*) AS count
FROM loans
WHERE user_id = 1 AND status = 1
UNION ALL
SELECT COUNT(*) AS count
FROM deals
WHERE user_id = 1 AND status = 1
UNION ALL
SELECT COUNT(*)
FROM listings
WHERE user_id = 1 AND status = 1
) AS x
DEMO
It is not 100% clear to me what you are trying to check.
But if I understand it correctly, I think the problem you have, is you are only checking for the user_id = 1, which might not have status 1 in all the tables (I really can't be sure without seeing your data).
I think you want to do something like:
SELECT COUNT(*) AS active_items
FROM loans
INNER JOIN deals ON deals.user_id = loans.user_id
INNER JOIN listings ON listings.user_id = loans.user_id
WHERE
loans.status = 1
AND deals.status = 1
AND listings.status = 1

SQL multi table query

I have three relations: Recipes(name, descr), Ingredients(recipeName, num, val) and Steps(recipeName, num, val). Each recipe will have one or more ingredients, and one or more steps. I am trying to write a query that will list the ingredients and steps for a single recipe. With this query
SELECT Recipes.name, Recipes.descr, Ingredients.num, Ingredients.val, Steps.num, Steps.val
FROM Recipes
LEFT OUTER JOIN Ingredients ON Recipes.id = Ingredients.recipeName
LEFT OUTER JOIN Steps ON Recipes.id = Steps.recipeName;
This query is like a cross product. So if my ingredients values for some recipe are 1,ing-a and 2,ing-b and steps values are 1,step-c, 2,step-d and 3,step-e, I get (order is Recipe.name, recipe.descr, Ingredients.num, Ingredients.val, Steps.num, Steps.val)
name, descr, 1, ing-a, 1, step-c
name, descr, 1, ing-a, 2, step-d
name, descr, 1, ing-a, 3, step-e
name, descr, 2, ing-b, 1, step-c
name, descr, 2, ing-b, 2, step-d
name, descr, 2, ing-b, 3, step-e
Is there a query that would return as shown below (recipe vals, then ingredients, then steps)
name
descr
1,ing-a
2,ing-b
1,step-c
2,step-d
3,step-e
Data
DROP TABLE iF EXISTS Recipes;
DROP TABLE iF EXISTS Ingredients;
DROP TABLE iF EXISTS Steps;
CREATE TABLE Recipes
(id INT,
name VARCHAR(100),
descr VARCHAR(100));
CREATE TABLE Ingredients
(recipeName INT,
num INT,
val VARCHAR(100));
CREATE TABLE Steps
(recipeName INT,
num INT,
val VARCHAR(100));
INSERT INTO Recipes VALUES (1, 'name', 'descr');
INSERT INTO Ingredients VALUES (1, 1, 'ing-a');
INSERT INTO Ingredients VALUES (1, 2, 'ing-b');
INSERT INTO Ingredients VALUES (1, 3, 'ing-c');
INSERT INTO Steps VALUES (1, 1, 'step-a');
INSERT INTO Steps VALUES (1, 2, 'step-b');
INSERT INTO Steps VALUES (1, 3, 'step-c');
Solution
SELECT v1.display
FROM ( SELECT Recipes.id AS sort_key, 1 AS seq, Recipes.name AS display
FROM Recipes
UNION ALL
SELECT Recipes.id AS sort_key, 2 AS seq, Recipes.descr AS display
FROM Recipes
UNION ALL
SELECT Ingredients.recipeName AS sort_key, 3 AS seq, CONCAT(Ingredients.num , ',' , Ingredients.val) AS display
FROM Ingredients
UNION ALL
SELECT Steps.recipeName AS sort_key, 4 AS seq, CONCAT(Steps.num , ',' , Steps.val) AS display
FROM Steps ) v1
ORDER BY v1.sort_key, v1.seq, v1.display;
Output
***display***
name
descr
1,ing-a
2,ing-b
3,ing-c
1,step-a
2,step-b
3,step-c
I just saw the question changed The following might not be valid.
You can use group concat to do this, like this:
SELECT
Recipes.name, Recipes.descr,
GROUP_CONCAT(CONCAT(Ingredients.num,' ',Ingredients.val) SEPARATOR ', ') AS Ingredients,
GROUP_CONCAT(CONCAT(Steps.num,' ',Steps.val) SEPARATOR ', ') AS Steps
FROM Recipes
LEFT OUTER JOIN Ingredients ON Recipes.id = Ingredients.recipeName
LEFT OUTER JOIN Steps ON Recipes.id = Steps.recipeName;
GROUP BY Recipes.name, Recipes.descr

How to find the column having duplicate value in SQL Server

I have a table:
create table #t
(
ID int,
value nvarchar(5)
)
insert #t
values (1,'A'), (2, 'B'), (3, 'A'), (3, 'B')
Sample data:
ID value
------------
1 A
2 B
3 A
3 B
For my project I need the ID which has having both the values
Result :
ID
3
Kindly help me out.
To get IDs having 2 values
select id
from #t
group by id
having count(distinct value) >= 2
or to get all IDs having A and B
select id
from #t
where value in ('A','B')
group by id
having count(distinct value) = 2
or to make it more generic to get IDs having all values
select id
from #t
group by id
having count(distinct value) = (select count(distinct value) from #t)

MYSQL - Updating count in one tabled based on two other tables joined

I have 3 related tables. Adults, Children and AC. Adults contains an INT column to count high school seniors. Children contains a column with year of highs school graduation. AC links the adult.id to the children.id.
CREATE TABLE adults (
id INT,
name VARCHAR(10),
seniors INT DEFAULT 0
) ;
INSERT INTO adults (id, name) VALUES
(1, 'adam'),
(2, 'bob');
CREATE TABLE children (
id INT,
name VARCHAR(10),
grad VARCHAR(4)
) ;
INSERT INTO children (id, name, grad) VALUES
(1, 'sally', '2016'),
(2, 'johnny', '2017'),
(3, 'eric', '2016'),
(4, 'billy', '2016'),
(5, 'rachel', '2016');
CREATE TABLE pc (
id INT,
a_id INT,
c_id INT
) ;
INSERT INTO pc (id, a_id, c_id) VALUES
(1, 1, 1),
(2, 1, 2),
(3, 1, 3),
(4, 2, 3),
(5, 2, 2);
SQLFiddle: http://sqlfiddle.com/#!2/89281e
So I want to update adults.seniors to the count of '2016' children they're linked to. So adult #1 would be "2" (sally and eric), and adult #2 "1" (eric).
The real data will be run across 25,000+ children being matched up to 40,000+ parents with a row count on the "pc" table above 3,000,000 rows - so looking for efficiency. I started working down this path but a) it's not working for obvious reasons and b) I doubt it would be efficient...
UPDATE adults a SET
seniors = (
SELECT p.a_id, count(*)
FROM pc p
INNER JOIN children c ON c.id = p.c_id
WHERE c.grad = '2016'
GROUP BY p.c_id)
WHERE p.a_id = a.id;
I'm thinking there has to be a better way of doing this with joins but can't seem to wrap my head around it.
You should be looking for this update statement:
UPDATE adults a
JOIN
(SELECT
p.a_id, COUNT(*) childrencount
FROM
pc p
INNER JOIN children c ON c.id = p.c_id
WHERE
c.grad = '2016'
GROUP BY p.a_id) c ON (a.id = c.a_id)
SET
seniors = c.childrencount;

Is it possible to group rows twice in MySQL?

I have a table like this:
someid somestring
1 Hello
1 World
1 Blah
2 World
2 TestA
2 TestB
...
Currently I'm grouping by the id and concatenating the strings, so I end up with this:
1 Hello,World,Blah
2 World,TestA,TestB
...
Is it possible to do a second grouping so that if there are multiple entries that end up with the same string, I can group those too?
Yes, just put your current query in an inner select and apply a new GROUP BY to the outer select. Note that you will probably want to use ORDER BY of GROUP_CONCAT to ensure that the strings are always concatenated in the same order.
SELECT somelist, COUNT(*) FROM
(
SELECT
someid,
GROUP_CONCAT(somestring ORDER BY somestring) AS somelist
FROM table1
GROUP BY someid
) AS T1
GROUP BY somelist
Result:
'Blah,Hello,World', 1
'TestA,TestB,World', 2
Here's the test data I used:
CREATE TABLE table1 (someid INT NOT NULL, somestring NVARCHAR(100) NOT NULL);
INSERT INTO table1 (someid, somestring) VALUES
(1, 'Hello'),
(1, 'World'),
(1, 'Blah'),
(2, 'World'),
(2, 'TestA'),
(2, 'TestB'),
(3, 'World'),
(3, 'TestB'),
(3, 'TestA');