SQL SELECT multiple keys/values - sql

I've got a table PERSON_PROPERTIES that resembles the following :
| ID | KEY | VALUE | PERSON_ID |
| 1 | fname | robert | 1 |
| 2 | lname | redford | 1 |
| 3 | fname | robert | 2 |
| 4 | lname | de niro | 2 |
| 5 | fname | shawn | 3 |
| 6 | nname | redford | 3 |
I would like to SELECT (in JPQL or in PSQL) the PERSON_ID that matches the given fname and lname.
I've tried
`SELECT DISTINCT *
FROM PERSON_PROPERTIES t0
WHERE ((((t0.key = 'fname')
AND (t0.value = 'robert'))
AND ((t0.key = 'lname')
AND (t0.value = 'redford'))))`
but it returns me no value.
I've also tried
`SELECT DISTINCT *
FROM PERSON_PROPERTIES t0
WHERE ((((t0.key = 'fname')
AND (t0.value = 'robert'))
OR ((t0.key = 'lname')
AND (t0.value = 'redford'))))`
but this way it returns me all values. I don't know how to turn the query properly for it to give me only value 1.

SELECT PERSON_ID
FROM PERSON_PROPERTIES
group by PERSON_ID
having sum(case when key = 'fname' and value = 'robert' then 1 else 0 end) > 0
and sum(case when key = 'lname' and value = 'redford' then 1 else 0 end) > 0
Groupy by the person and select only those having both values.

Another approach would be with subselect (caution, it's MS SQL 2012)
SELECT PERSON_ID
FROM PERSON_PROPERTIES
WHERE [Key] = 'fname' AND value = 'robert'
AND PERSON_ID in
(SELECT PERSON_ID FROM PERSON_PROPERTIES WHERE [Key] = 'lname' AND value = 'redford')
Fiddle Demo

Along with some colleagues we came to this answer :
SELECT p.PERSON_ID
FROM PERSON_PROPERTIES p
WHERE (p.key = 'fname' AND p.value = 'robert')
OR (p.key = 'lname' AND p.value = 'redford')
GROUP BY p.PERSON_ID
HAVING count(*) = 2
What do you think about it?

SELF JOIN also does the trick. DISTINCT for duplicate person_id:
SELECT DISTINCT a.PERSON_ID
FROM PERSON_PROPERTIES a JOIN PERSON_PROPERTIES b ON a.PERSON_ID = b.PERSON_ID
WHERE a.the_key = 'fname' AND a.value = 'robert'
AND b.the_key = 'lname' AND b.value = 'redford';
Demo

OK I will be marking this as the correct answer. The only thing I did was modified it a bit
SELECT Y.*, M.* FROM wp_postmeta as Y JOIN wp_postmeta AS M USING (`post_id`)
WHERE (Y.meta_key = 'agam_post_options_year' AND Y.meta_value = 2013)
AND (M.meta_key = 'agam_post_options_month' AND M.meta_value BETWEEN 0 AND 12 )
GROUP BY Y.meta_value, M.meta_value ORDER BY M.meta_value+0 DESC
So I get that DESC order.. however.. I noticed that it does not duplicates results... I had two posts with the same year and same month... now I don't see it... is there anything there that's preventing this ?

Related

How can I use SQL Query to attain a complete address from a database?

I have got a database with addresses and postcodes. I need to just get the complete address. How can I do this in a SQL Query? I tried row count, that did not work as some people can move a couple of times. The data is in the below format, I want the none matching records from the table.
+-------+---------+----------+-------------------------+----------+-------+-----------+--------------------------------------+
| ID | Percode | ITEM | MoveSTART | TRACKEND | PID | REFERENCE | TRACKVALUE |
+-------+---------+----------+-------------------------+----------+-------+-----------+--------------------------------------+
| 14051 | 0 | ADDRESS | 2011-03-24 00:00:00.000 | NULL | 10000 | NULL | 1 test High Street Avening, test |
| 14050 | 0 | POSTCODE | 2014-05-27 00:00:00.000 | NULL | 10000 | NULL | S12 8DU |
| 14049 | 0 | POSTCODE | 2011-03-24 00:00:00.000 | NULL | 10000 | NULL | s12 8LU |
+-------+---------+----------+-------------------------+----------+-------+-----------+--------------------------------------+
i want to return only ID -14050, as the other 2 are complete address
SELECT ID, Percode, ITEM, MoveSTART, TRACKEND, PID, VALUE
FROM tyeaddtest
where MoveSTART not in (select MoveSTART from tyeaddtest where ITEM in ('ADDRESS')) and ITEM = 'POSTCODE'
Some i have returned with the above query, but similar to the above table i cant return them with this query
Yes PID and Movestart are the same so i have extra Postcode lines i need only return all the onces that dont have an address in Item
Here's a slightly inelegant way of returning any PID and MoveStart combination that doesn't have exactly one address and postcode:
select pid, movestart,
(select count(*) from tyeaddtest a where a.pid = t.pid and a.movestart = t.movestart and item = 'ADDRESS') as addresses,
(select count(*) from tyeaddtest p where p.pid = t.pid and p.movestart = t.movestart and item = 'POSTCODE') as postcodes
from tyeaddtest t
group by pid, movestart
having (select count(*) from tyeaddtest a where a.pid = t.pid and a.movestart = t.movestart and item = 'ADDRESS') != 1
or (select count(*) from tyeaddtest p where p.pid = t.pid and p.movestart = t.movestart and item = 'POSTCODE') != 1
If you can use common table expressions with your database then you could make this a bit nicer and avoid having to repeat the subqueries, e.g. this is the syntax for SQL Server:
with AddressAndPostcodeCounts as (
select pid, movestart,
(select count(*) from tyeaddtest a where a.pid = t.pid and a.movestart = t.movestart and item = 'ADDRESS') as addresses,
(select count(*) from tyeaddtest p where p.pid = t.pid and p.movestart = t.movestart and item = 'POSTCODE') as postcodes
from tyeaddtest t
group by pid, movestart
)
select * from AddressAndPostcodeCounts where addresses != 1 or postcodes != 1
but you havent told us which database you're using.
Do you mean something like
SELECT DISTINCT t1.PID, (SELECT TOP 1 t2.VALUE FROM the_table_name t2
WHERE t2.item='ADDRESS' AND t2.PID = t1.PID ORDER BY MoveSTART DESC) as ADDRESS
from the_table_name t1
If you're using MySQL you need to replace TOP 1 with LIMIT 1 at the end of the subquery

SQL statement select columns with specific value

I need some help making an sql statement; I don't really know how to aproach the situation. I have two tables, Departments and Employees
from which I want to select the Dpt_num and the Dpt_name of the departments that have at least one employee and that all their employees are from Barcelona
Case 1
== Departments =======
| Dpt_num | Dpt_name |
| 1 | A |
| 2 | B |
== Employees ===================
| E_num | Dpt_num | City |
| 1 | 1 | Barcelona |
| 2 | 1 | Barcelona |
The result in this case should be
Dpt_num Dpt_name
------------------
1 A
Case 2
== Departments =======
| Dpt_num | Dpt_name |
| 1 | A |
| 2 | B |
== Employees ==================
| E_num | Dpt_num | City |
| 1 | 1 | Barcelona |
| 2 | 1 | Madrid |
The result in this case should be empty.
I tried this for example but it seems very inefficient and it does not work in all the cases
select
num_dpt, nom_dpt
from
departements
where
1 = (select count(distinct e.ciutat_empl)
from empleats e
where e.num_dpt = num_dpt)
and not exists (select * from empleats e
where e.ciutat_empl != 'BARCELONA' and e.num_dpt = num_dpt);
I really appreciate any help. Thanks!
You want to go down the path of doing the filtering in the where clause. Then, use exists and not exists:
select d.num_dpt, d.nom_dpt
from departaments d
where exists (select 1
from empleats e
where e.num_dpt = d.num_dpt and e.ciutat_empl = 'BARCELONA'
) and
not exists (select 1
from empleats e
where e.num_dpt = d.num_dpt and e.ciutat_empl <> 'BARCELONA'
);
The first condition checks that at least one employee is from Barcelona. The second checks that no employees are from any other city.
One major problem in your version is your correlation clause:
e.num_dpt = num_dpt
You think this is doing:
e.num_dpt = departaments.num_dpt
But it is really doing:
e.num_dpt = e.num_dpt
Always qualify your column names. This is especially important when you have more than one table reference in the query.
Join the tables, group by the department and check if the count of employees in Barcelona is equal to the count of all employess of the department.
SELECT d.dpt_num,
d.dpt_name
FROM departments d
INNER JOIN employees e
ON e.dpt_num = d.dpt_num
GROUP BY d.dpt_num,
d.dpt_name
HAVING count(CASE
WHEN e.city = 'Barcelona' THEN
1
END) = count(*);
I believe this should work:
select d.dpt_num, d.dpt_name
from departments d
inner join employees e on
d.dpt_num = e.dpt_num
group by d.dpt_num, d.dpt_name
having count(*) = sum(case when e.city = 'Barcelona' then 1 else 0 end)
INNER JOIN makes sure there's at least 1 employee
HAVING count(*) = sum(case when e.city = 'Barcelona' then 1 else 0 end) makes sure that all employees are from Barcelona
demo: db<>fiddle
SELECT dpt_num, dpt_name
FROM (
SELECT d.dpt_num, d.dpt_name, array_agg(city) as cities
FROM dept d
JOIN empl e
ON d.dpt_num = e.dpt_num
GROUP BY d.dpt_num, d.dpt_name
) s
WHERE 'Barcelona' = ALL(cities)
Aggregate the cities and then you can filter with the ALL operator which checks if all array elements fit the condition.
Generally speaking, you compare COUNT(*) with COUNT(some condition) for such problems:
SELECT *
FROM Departments
WHERE EXISTS (
SELECT 1
FROM Employees
WHERE Employees.Dpt_num = Departments.Dpt_num
HAVING COUNT(*) > 0 -- it is possible to get a 0 if where did not match
AND COUNT(*) = COUNT(CASE WHEN Employees.City = 'Barcelona' THEN 1 END)
)
DB Fiddle
Pl try query below
select a.dpt_number,a.dpt_name from yy_department a
where exists (select 'x' from yy_employees y where y.dpt_number = a.dpt_number and y.city = 'Barcelona')
and not exists (select 'x' from yy_employees y where y.dpt_number = a.dpt_number and nvl(y.city,'x') <> nvl('Barcelona','y'))

Marking duplicates records in a table

I am trying to mark duplicate records, however I get wrong reassignment on few on them and I don't know why.
Data:
=FirstName | LastName | Company | Group | Status | ID
x | x | x | NULL | NULL | 1
x | x | x | NULL | NULL | 2
Then I run this query to find matches on FirstName, LastName, Company
and join it back to the main table to mark the records:
with d as (
select ID, FirstName, LAstName, Company, row_number() over (partition by FirstName,LastName, Company order by FirstName,LastName, Company) as nr
from [dbo].xx)
Update b
set Status = 'S'
, Group = d.DQ_ID
from xx as b inner join d on
b.FirstName = d.FirstName and
b.LastNAme = d.LastName and
b.Company = d.Company
where d.nr = 1
And then Update the Main Record with P
Update b
set Status = 'P'
from xx as b
where b.ID = b.Group
GO
What I expect:
=FirstName | LastName | Company | Group | Status | ID
x | x | x | 1 | P | 1
x | x | x | 1 | S | 2
What I get:
=FirstName | LastName | Company | Group | Status | ID
x | x | x | 2 | S | 1
x | x | x | 1 | S | 2
I am working on about 1M records - and it only happen to some of them!
Try this :
;with d as (
select
ID,
FirstName,
LAstName,
Company,
row_number() over (
partition by FirstName,LastName, Company
order by Id asc -- this was done to keep ordering as per ID
) as nr
from [dbo].xx
) ,
e as
(select * from d where nr=1)
-- e was created to only take the nr=1 rows which will be joined to all similar records
Update b
set Status = case when e.DQ_ID = b.DQ_ID then 'P' else 'S' end
-- the set case logic ensures that matching ids get P else S
, Group = e.DQ_ID
from xx as b
inner join e on
b.FirstName = e.FirstName and
b.LastNAme = e.LastName and
b.Company = e.Company
Can try with the following:
;WITH RankedData AS
(
SELECT
T.ID,
T.[Group],
T.Status,
T.FirstName,
T.LastName,
T.Company,
GroupRanking = ROW_NUMBER() OVER (PARTITION BY T.FirstName, T.LastName, T.Company ORDER BY T.ID ASC)
FROM
dbo.xx AS T
)
UPDATE T SET
[Group] = N.ID,
Status = CASE WHEN T.GroupRanking = 1 THEN 'P' ELSE 'S' END
FROM
RankedData AS T
INNER JOIN RankedData AS N ON
T.FirstName = N.FirstName AND
T.LastName = N.LastName AND
T.Company = N.Company AND
N.GroupRanking = 1
Keep in mind that the INNER JOIN will join on not null names and companies, will have to keep in mind if you have nulls on those columns.

sql check if join table has rows by mutliple criterias

I have two tables, assets and asset_params.
assets
|asset_id| some_asset_data |
-----------------------------
| 1 | 'some data' |
| 2 | 'some other data'|
asset_params
|asset_param_id|asset_id|param_name|param_value|
------------------------------------------------
| 1 | 1 | 'Memory' | '4096' |
| 2 | 1 | 'OS' | 'Windows' |
| 3 | 2 | 'Memory' | '4096' |
| 4 | 2 | 'OS' | 'Linux' |
Now, how can i find assets which have parameters where they have a param called 'Memory' with value '4096' AND a param 'OS' with value 'Windows'.
Desired result is that i find one asset row, with id 1, in the example case.
I cant find a reasonable solution. Any help is appreciated.
select * from assets a where
exists (select 1 from asset_params p
where name = "OS" and value = "Win" and a.asset_id=p.asset_id)
and exists (select 1 from asset_params p
where name = "memory" and value = "4096" and a.asset_id=p.asset_id)
You can do this with aggregation and a having clause:
select asset_id
from asset_params
where param_name = 'Memory' and param_value = '3096' or
param_name = 'OS' and param_value = 'Windows'
group by asset_id
having count(*) = 2;
Note: if you can have multiple parameters with the same name, you should use count(distinct param_name) = 2.
This easily generalized. A more Postgres'ish way of writing it is:
select asset_id
from asset_params
where (param_name, param_value) in (('Memory', '3096'), ('OS', 'Windows'))
group by asset_id
having count(*) = 2;
Something like this should work:
select * from assets where
assets_id in (select assets_id from asset_params
where name = "OS" and value = "Win")
and
assets_id in (select assets_id from asset_params
where name = "memory" and value = "4096")
If your tables are huge, this needs proper indexes. But you should always have good indexes anyway. ;)

Select from other table if value exist

I created a fiddle for this, at this link:
http://www.sqlfiddle.com/#!2/7e007
I could'nt find SQL compact / CE so it's in MySQL.
The tables looks like this
Records Clients
ID | NAME | AGE ID | NAME
------------------ ----------------
1 | John | 20 1 | John
2 | Steven | 30 2 | Daniel
3 | Abraham | 30 3 |
4 | Donald | 25 5 | Lisa
6 | | 35 6 | Michael
7 | | 42 7 |
I would like to select from both tables, and if the id is in both tables and both have names I would like the the name from "Clients" as the default. If the name in Records is blank, use the client name (if any) and if the Clients.Name is blank; use the records.Name.
From tables above, i would like this:
ID | NAME | AGE
------------------
1 | John | 20
2 | Daniel | 30
3 | Abraham | 30
4 | Donald | 25
5 | Lisa |
6 | Michael | 35
7 | | 42
How do i do this in SQL Compact?
EDIT:
Thanks to great answers below i've managed to come up with this query, which ALMOST works:
SELECT t.id, t.name, t.age FROM
(
(
SELECT r.id,
CASE WHEN r.name = NULL THEN c.name ELSE r.name END as name,
r.age
FROM Records r
LEFT JOIN Clients c ON c.id = r.id
)
UNION
(
SELECT c.id, c.name, null as age FROM Clients c where c.id NOT IN (select id from Records)
)
) as t ORDER BY t.id
This gives me this output:
ID | NAME | AGE
------------------
1 | John | 20
2 | Daniel | 30
3 | Abraham | 30
4 | Donald | 25
5 | Lisa |
6 | | 35
7 | | 42
"Michael" (should be on #6) is missing in this case. Why?!
select r.id,
IF(c.name != '',c.name,r.name) as name,
r.age
FROM Records r
LEFT JOIN Clients c ON c.id = r.id
GROUP BY c.id
Use above query.
EDITED:
SELECT t.id, t.name, t.age FROM
(
(
SELECT r.id,
CASE WHEN c.name <> '' THEN c.name ELSE r.name END as name,
r.age
FROM Records r
LEFT JOIN Clients c ON c.id = r.id
)
UNION
(
SELECT c.id, c.name, null as age FROM Clients c where c.id NOT IN (select id from Records)
)
) as t ORDER BY t.id
Use this query.
please try,hope this will work..
select c.id,
IF(NAME='',(select name from Records where id = c.id),'')
If(NAME=NULL,(select name from Records where id = c.id),NULL)
Else c.NAME
from client c;
cheers!!!
select case when a.id <> '' then a.id else b.id end as id ,
case when a.name <> '' then a.name else b.name end as name,a.age
from records a
full outer join clients b on a.Id = b.id
order by a.id
Use COALECSE to get the first not-null value:
select id, coalesce(clients.name, records.name) as correct_name, records.age
from records
join clients using (id);
EDIT: In case not existing names are stored as '' instead of NULL use:
select id, case when clients.name = '' then records.name else clients.name end as correct_name, records.age
from records
join clients using (id);
Of course you can also react on both '' and NULL by asking
when clients.name = '' or clients.name is null then
See http://www.sqlfiddle.com/#!2/7e007/36.