Using fields from select query in where clause in subqueries - tsql

I have a list of people and there are 4 types that can occur as well as 5 resolutions for each type. I'm trying to write a single query so that I can pull each type/resolution combination for each person but am running into problems. This is what I have so far:
SELECT person,
TypeRes1 = (SELECT COUNT(*) FROM table1 where table1.status = 45)
JOIN personTbl ON personTbl.personid = table1.personid
WHERE person LIKE 'A0%'
GROUP BY person
I have adjusted column names to make it more...generic, but basically the person table has several hundred people in it and I just want A01 through A09, so the like statement is the easiest way to do this. The problem is that my results end up being something like this:
Person TypeRes1
A06 48
A04 48
A07 48
A08 48
A05 48
Which is incorrect. I can't figure out how to get the column count correct for each person. I tried doing something like:
SELECT person as p,
TypeRes1= (SELECT COUNT(*) FROM table1
JOIN personTbl ON personTbl.personid = table1.personid
WHERE table1.status = 45 AND personTbl.person = p)
FROM table1
JOIN personTbl ON personTbl.personid = table1.personid
WHERE personTbl.person LIKE 'A0%'
GROUP BY personTbl.person
But that gives me the error: Invalid Column name 'p'. Is it possible to pass p into the subquery or is there another way to do it?
EDIT: There are 19 different statuses as well, so there will be 19 different TypeRes, for brevity I just put the one as if I can find the one, I think I can do the rest on my own.

Maybe something like this:
SELECT
person,
(
SELECT
COUNT(*)
FROM
table1
WHERE
table1.status = 45
AND personTbl.personid = table1.personid
) AS TypeRes1
FROM
personTbl
WHERE person LIKE 'A0%'

Related

T-SQL select all IDs that have value A and B

I'm trying to find all IDs in TableA that are mentioned by a set of records in TableB and that set if defined in Table C. I've come so far to the point where a set of INNER JOIN provide me with the following result:
TableA.ID | TableB.Code
-----------------------
1 | A
1 | B
2 | A
3 | B
I want to select only the ID where in this case there is an entry for both A and B, but where the values A and B are based on another Query.
I figured this should be possible with a GROUP BY TableA.ID and HAVING = ALL(Subquery on table C).
But that is returning no values.
Since you did not post your original query, I will assume it is inside a CTE. Assuming this, the query you want is something along these lines:
SELECT ID
FROM cte
WHERE Code IN ('A', 'B')
GROUP BY ID
HAVING COUNT(DISTINCT Code) = 2;
It's an extremely poor question, but you you probably need to compare distinct counts against table C
SELECT a.ID
FROM TableA a
GROUP BY a.ID
HAVING COUNT(DISTINCT a.Code) = (SELECT COUNT(*) FROM TableC)
We're guessing though.

How does one avoid Join placing a constraint on aggregate function?

[using postgres]
So the background to my scenario is:
I'm trying to get the average expenses by age where the user is active
cost = 40
Only 2 of the 33 year olds have purchased something
I have 34 active members that are 33 years old and active (whether or not they made a payment is irrelevant in this count)
with this in mind money spent per age = 40 / 34 = 1.18
what I am getting right now is = 40 / 2 = 20
I understand that it's constrained by the two users who made a purchase
So where did I get all of this?
select date_part('year', age(birthday)) as age,
avg(cost)
from person
inner join payment on person.person_id = payment.person_id
inner join product on payment.product_id = product.product_id
where
date_part('year', age(birthday))= 33 and user_state = 'active'
group by age
Unfortunately, when using an aggregate function (in this example avg())
it seems avg() is constrained to the result of the inner join (I've tried a left join to maintain having access to all users, it didn't seem to work since I still got the undesired result 20). Is there a way to avoid this? In other words can I make it so the avg() call is specific to my person table rather than the result of the join?
If it matters, this is how I am retrieving total sum.
select sum(cost)
from person
inner join payment on person.person_id = payment.person_id
inner join product on payment.product_id = product.product_id
where
date_part('year', age(birthday))= 33
and
user_state = 'active'
= 40
The obvious is to do the count of people I want and then do the sum seperately, but I'm trying to avoid going from one query to another.
avg will skip nulls so coalesce those null values into zeros and obviously left join:
select
date_part('year', age(birthday)) as age,
avg(coalesce(cost,0))
from
person
left join
payment on ...
left join
product on ...

Find equal twin record postgresql

I have a table company with 60 columns. The goal is to create a tool to find, compare and eliminate duplicates in this table.
Example: I have a record with id 22 and I know it has a twin because I run this (simplified code):
SELECT min(co_id),co_name,count(*) FROM co
GROUP BY co_name
HAVING count(*) > 1
The result shows there are one twin (count 2) and I get the oldest id by min(co_id)
My question is how I search for the twin co_id? Just passing the oldest id?
Something like:
SELECT co_id FROM co
WHERE co_name EQUAL TO co_id='22'
LIMIT 2
Sample data:
id co_name
22 Volvo
23 Volvo
24 Ford
25 Ford
I know id 22 and I want to search for the twin 23 based on the content of 22.
The closest I found is this. Which is far from generic. And a nightmare for comparing 60 field:
SELECT id,
(SELECT max(b.id) from co b
WHERE a.co_name = b.co_name
LIMIT 1) as twin
FROM co a
WHERE id='22'
How do I do this in a more simple and generic way? I just want the twin record co_id.
Thank you in advance!
select max_co,co_name from (
select max(co_id) max_co,min(co_id) min_co,co_name from co
group by co_name having count(*)>1) where min_co=(your old co id as input);
You can join your table with itself:
SELECT c1.*
FROM
co_name c1 INNER JOIN co_name c2
ON c1.co_name=c2.co_name
AND c1.id>c2.id
this will return all duplicated records (but not the original record with the lowest id). Or since you're using Postgresql you can use a window function:
SELECT *
FROM (
SELECT
id,
co_name,
row_number() OVER (PARTITION by co_name ORDER BY id) as row
FROM
co_name
) s
WHERE
row>1;
Please see an example here.
If you want to compare multiple columns, the JOIN solution would be more flexible. I don't know exactly how you want to compare your columns and how you exactly define "twin" rows, but you a query like this should help:
SELECT c1.*
FROM
co_name c1 INNER JOIN co_name c2
ON (
c1.co_name=c2.co_name
OR c1.co_city=c2.co_city
OR c1.co_owner=c2.co_owner
OR ...
) AND c1.id>c2.id
if you just want duplicated records of id=22 then you can try with this:
SELECT c1.*
FROM
co_name c1 INNER JOIN co_name c2
ON c1.co_name=c2.co_name
AND c1.id>c2.id
WHERE
c2.id=22
or if you just want a single twin, comparing 60 columns, you can try with this query:
SELECT MIN(ID) as Twin /* or MAX(ID), depending what you're after */
FROM
co_name c1 INNER JOIN co_name c2
ON (
c1.co_name=c2.co_name
OR c1.co_city=c2.co_city
OR c1.co_owner=c2.co_owner
OR ...
) AND c1.id>c2.id
WHERE
c2.id=22
I found one solution that is working on 60 columns if I use variables in stead of hardcode in the query. Thanks everybody for all input. Some of them were about the same track.
SELECT id,
(SELECT max(b.id) from co b
WHERE concat(a.co_name,etc) = concat(b.co_name,etc)
LIMIT 1) as twin
FROM co a
WHERE id='22'
Not the best one, but fetch one twin at a time. And it is far from generic. Thanks for pointing me in the right direction. A generic solution would be nicer.

SUM of COUNTs in the same table

I'm doing many counts that I want to show in a table. And I want in the same table to show the sum of all counts.
Here's what I got (simplified - I got 6 Counts):
SELECT * FROM (SELECT COUNT() AS NB_book
item as a1, metadatavalue as m1, metadatavalue as m12,
WHERE m1.field_id = 64 (because I need that field to exist)
AND m2.field_id = 66
And m2. = book
AND a1.in_archive = TRUE )
(SELECT COUNT() AS NB_toys
metadatavalue as m1, metadatavalue as m12,
WHERE m1.field_id = 64 (because I need that field to exist)
AND m2.field_id = 66
And m2. = toys
AND a1.in_archive = TRUE)
)
Now, I want the display to be like
-------------table ----------
|NB_book | NB_Toys | total_object |
-----------------------------
| 12 | 10 | 22 |
You want something along the lines of:
SELECT
sum(CASE WHEN condition_1 THEN 1 END) AS firstcount,
sum(CASE WHEN condition_2 THEN 1 END) AS secondcount,
sum(thecolumn) AS total
FROM ...
Your example query is too vague to construct something usable from, but this'll give you the idea. The conditions above can be any boolean expression.
If you prefer you can use NULLIF instead of CASE WHEN ... THEN ... END. I prefer to stick to the standard CASE.
It is difficult to figure out what you actually want. You can run completely different queries that each return a one-row result and combine the results like this:
select
(select count(*) from pgbench_accounts) as count1,
(select count(*) from pgbench_tellers) as count2 ;
But perhaps you shouldn't do that. Instead just run each query by itself and use the client, rather than the database engine, to format the results.

What's the best T-SQL syntax to filter for an ID that has a count of X or at least X or at most X in a joined table?

What's the best way to do something like this in T-SQL?
SELECT DISTINCT ID
FROM Members,
INNER JOIN Comments ON Members.MemberId = Comments.MemberId
WHERE COUNT(Comments.CommentId) > 100
Trying to get the members who have commented more than 100 times. This is obviously invalid code but what's the best way to write this?
This should get you what you're after. I'm not saying this is the absolutely best way of doing it, but it's unlikely you'll find anything better.
SELECT ID
FROM Members
INNER JOIN Comments
ON Members.MemberId = Comments.MemberId
GROUP BY ID
HAVING COUNT(*) > 100
I like using a subquery.
SELECT DISTINCT m.ID
FROM Members m
WHERE (SELECT COUNT(c.CommentID)
FROM Comments c
WHERE c.MemberID = m.MemberID) > 100
Try
SELECT ID
FROM Members
INNER JOIN (SELECT MemberID FROM Comments
GROUP BY MemberID HAVING COUNT(CommentId) > 100)
AS CommentCount ON Members.MemberID = CommentCount.CommentID