SQL preferred one-to-many relationship - sql

Say I have a one-to-many relationship between clients and addresses.
Clients can have multiple addresses with different address types (eg. home, postal, work, company, future), and might have no address or multiple addresses of the same type (in which case I'm happy to take the 1st occurrence).
My data consists of clientid, address, and addresstypeid. The preferred order of the addresstypeid is 2,3,4,1: so if a client has an addresstypeid of 2 return that, if not return the record with 3, if not 4, if not 1 and if not null.
Is there an more elegant way to do this than something like this?
declare #addresses table
(
clientid int,
address varchar(10),
addresstypeid int
)
--2,3,4,1
insert into #addresses (clientid, address, addresstypeid)
select 1, '1a', 1 union all
select 1, '1b', 2 union all
select 1, '1c', 3 union all
select 1, '1d', 4 union all
select 2, '2a', 2 union all
select 2, '2b', 3 union all
select 2, '2c', 4 union all
select 2, '2a', 1 union all
select 3, '3a', 3 union all
select 3, '3b', 4 union all
select 3, '3c', 1 union all
select 4, '4a', 3 union all
select 4, '4b', 4 union all
select 5, '5a', 4 union all
select 6, '6a', 4 union all
select 6, '6b', 1 union all
select 7, '7a', 1 union all
select 7, '7b', 4
declare #ranktable table
(
addresstypeid int,
rank int
)
insert into #ranktable(addresstypeid, rank)
select 2, 1 union all
select 3, 2 union all
select 4, 3 union all
select 1, 4
select
e.address,
e.clientid,
e.addresstypeid
from
#addresses e
inner join #ranktable r on
e.addresstypeid = r.addresstypeid
inner join (select
em.clientid,
min(rank) minrank
from #addresses em
inner join #ranktable ranks on
em.addresstypeid = ranks.addresstypeid
group by
clientid) clientranks on
e.clientid = clientranks.clientid and
r.rank = clientranks.minrank

The two tables are fine, though you should consider indexes when you make them permanent :)
In terms of your final query, I'd change it slightly...
WITH
sorted_data
AS
(
SELECT
[a].*,
ROW_NUMBER() OVER (PARTITION BY [a].clientid ORDER BY [r].rank) AS sequence_id
FROM
#addresses AS [a]
INNER JOIN
#ranktable AS [r]
ON a.addresstypeid = r.addresstypeid
)
SELECT
*
FROM
sorted_data
WHERE
sequence_id = 1

Related

Apply value to group

need help with,
if any ID with same Groupid has Yes in Payable, add Yes value to Results, otherwise blank.
This should be applicable for hundreds of IDs grouped in hundreds of GroupIDs.
ID
GroupID
Payable
Result
111
a
Yes
Yes
222
a
Yes
333
a
Yes
444
b
Yes
555
b
Yes
Yes
777
b
Yes
888
c
I tried to group based on groupID and created a case where groupId count equals or is higher as 1 and the eligibility is Yes.
Your question is a bit unclear due to lack of information about constraints, all the values in table etc.
With the given information tho, your task might be done by following query:
with groupData as
(
Select groupid,payable from put_your_table_name_here
where payable is not null
group by groupid
)
Select pt.id
,pt.groupId
,gd.payable
,pt.result
from put_your_table_name_here pt
left join groupData gd on gd.groupid=pt.groupid
The query has it drawbacks- you should give some more info about constraints, but generally it should work.
If you wouldnt want to have null values in payable column, you could change left join to join.
WITH CTE(ID, GroupID, Payable)AS
(
SELECT 111,'A','YES'
UNION ALL
SELECT 222,'A',''
UNION ALL
SELECT 333,'A',''
UNION ALL
SELECT 444,'B',''
UNION ALL
SELECT 555,'B','YES'
UNION ALL
SELECT 777,'B',''
UNION ALL
SELECT 888,'C',''
)
SELECT C.ID,C.GroupID,C.Payable,F.FLAG
FROM CTE AS C
JOIN
(
SELECT X.GROUPID,MAX(PAYABLE)FLAG
FROM CTE AS X
GROUP BY X.GroupID
)F ON C.GroupID=F.GroupID
ORDER BY C.ID;
You can try something like this
or
SELECT C.ID,C.GroupID,C.Payable,
FIRST_VALUE(C.PAYABLE)OVER(PARTITION BY C.GROUPID ORDER BY C.PAYABLE DESC)XCOL
FROM CTE AS C
ORDER BY C.ID;
with data (ID,GroupID,Payable) as (
Select 111, 'a', 'Yes' from dual union all
Select 222, 'a', null from dual union all
Select 333, 'a', null from dual union all
Select 444, 'b', null from dual union all
Select 555, 'b', 'Yes' from dual union all
Select 777, 'b', null from dual union all
Select 888, 'c', null from dual
)
,result as(
select GroupID, case when Count(*) > 1 then 'Yes' else null end Result
from data
group by GroupID)
Select * from data
Join result on data.GroupID = result.GroupID
order by data.ID,data.GroupID
Db fiddle link

How filter rows by matched values using BigQuery?

I have a table in BigQuery
SELECT 1 as big_id, 1 as temp_id, '101' as names
UNION ALL SELECT 1,1, 'z3Awwer',
UNION ALL SELECT 1,1, 'gA1sd03',
UNION ALL SELECT 1,2, 'z3Awwer',
UNION ALL SELECT 1,2, 'gA1sd03',
UNION ALL SELECT 1,3, 'gA1sd03',
UNION ALL SELECT 1,3, 'sAs10sdf4',
UNION ALL SELECT 1,4, 'sAs10sdf4',
UNION ALL SELECT 1,5, 'Adf105',
UNION ALL SELECT 2,1, 'A1sdf02',
UNION ALL SELECT 2,1, '345A103',
UNION ALL SELECT 2,2, '345A103',
UNION ALL SELECT 2,2, 'A1sd04',
UNION ALL SELECT 2,3, 'A1sd04',
UNION ALL SELECT 2,4, '6_0Awe105'
I want to filter it by temp_id if all names of one temp_id included in some another temp_id in partition by big_id window. For example I do not need to select all rows where temp_id = 2 because all names of temp_id = 2 included in temp_id = 1. As well as need to keep all rows of temp_id = 1 because this names range covers names range of temp_id = 2
So expected output:
SELECT 1 as big_id, 1 as temp_id, '101' as names
UNION ALL SELECT 1,1, 'z3Awwer',
UNION ALL SELECT 1,1, 'gA1sd03',
UNION ALL SELECT 1,3, 'gA1sd03',
UNION ALL SELECT 1,3, 'sAs10sdf4',
UNION ALL SELECT 1,5, 'Adf105',
UNION ALL SELECT 2,1, 'A1sdf02',
UNION ALL SELECT 2,1, '345A103',
UNION ALL SELECT 2,2, '345A103',
UNION ALL SELECT 2,2, 'A1sd04',
UNION ALL SELECT 2,4, '6_0Awe105'
How can I make it using BigQuery?
Below is for BigQuery Standard SQL
#standardsql
with temp as (
select big_id, temp_id, array_agg(names) names
from `project.dataset.table`
group by big_id, temp_id
)
select big_id, temp_id, names
from (
select big_id, temp_id, any_value(names) names
from (
select t1.*,
( select count(1)
from t1.names name
join t2.names name
using(name)
where t1.temp_id != t2.temp_id
) = array_length(t1.names) as flag
from temp t1
join temp t2
using (big_id)
)
group by big_id, temp_id
having countif(flag) = 0
), unnest(names) names
If to apply above to sample data from your question - the output is

How to compare column in one table with array from another table in BigQuery?

Just continue from the answer for my previous question.
I want to get all values from table b (in rows) if there is any difference between values in arrays from table a by same ids
WITH a as (SELECT 1 as id, ['123', 'abc', '456', 'qaz', 'uqw'] as value
UNION ALL SELECT 2, ['123', 'wer', 'thg', '10', '200']
UNION ALL SELECT 3, ['200']
UNION ALL SELECT 4, null
UNION ALL SELECT 5, ['140']),
b as (SELECT 1 as id, '123' as value
UNION ALL SELECT 1, 'abc'
UNION ALL SELECT 1, '456'
UNION ALL SELECT 1, 'qaz'
UNION ALL SELECT 1, 'uqw'
UNION ALL SELECT 2, '123'
UNION ALL SELECT 2, 'wer'
UNION ALL SELECT 2, '10'
UNION ALL SELECT 3, null
UNION ALL SELECT 4, 'wer'
UNION ALL SELECT 4, '234'
UNION ALL SELECT 5, '140'
UNION ALL SELECT 5, '121'
)
SELECT * EXCEPT(flag)
FROM (
SELECT b.*, COUNTIF(b.value IS NULL) OVER(PARTITION BY id) flag
FROM a LEFT JOIN a.value
FULL OUTER JOIN b
USING(id, value)
)
WHERE flag > 0
AND NOT id IS NULL
It works well for all ids except 5.
In my case I need to return all values if there is any difference.
In example array with id 5 from table a has only one value is '140' while there are two rows with values by id 5 from table b. So in this case all values by id 5 from table b also must appear in expected output
How need to modify this query to get what I want?
UPDATED
Seems like it works for me. But I can not be sure for 100%
SELECT * EXCEPT(flag)
FROM (
SELECT b.*, COUNTIF((b.value IS NULL AND a.value IS NOT NULL) OR (b.value IS NOT NULL AND a.value IS NULL)) OVER(PARTITION BY id) flag
FROM a LEFT JOIN a.value
FULL OUTER JOIN b
USING(id, value)
)
WHERE flag > 0
AND NOT id IS NULL
#standardSQL
SELECT *
FROM table_b
WHERE id IN (
SELECT id FROM table_a a
JOIN table_b b USING(id)
GROUP BY id
HAVING STRING_AGG(IFNULL(b.value, 'NULL') ORDER BY b.value) !=
IFNULL(ANY_VALUE((SELECT STRING_AGG(IFNULL(value, 'NULL') ORDER BY value) FROM a.value)), 'NULL')
)

How to specify the order results with a SQL UNION?

I'm trying to run the following query:
select * from (select * from customquestionbank where questionid=6 or secondquestionid=6
union select * from customquestionbank where questionid=5 or secondquestionid=5
union select * from customquestionbank where questionid=10 or secondquestionid=10
union select * from customquestionbank where questionid=11 or secondquestionid=11) Tabled
Being new to this site I cannot post images yet, but here is what the result look like:
questionid -> 5,6 ,10,11
However, I want the result to be displayed in the same order as my select statements above. In other words, questionid=6 returned first, then 5, and so on.
You don't need all the unions, just make it like this:
SELECT DISTINCT *
FROM customquestionbank
WHERE questionid IN ( 6, 5, 10, 11 )
OR secondquestionid IN ( 6, 5, 10, 11 )
ORDER BY CASE
WHEN 6 IN ( questionid, secondquestionid ) THEN 0
WHEN 5 IN ( questionid, secondquestionid ) THEN 1
WHEN 10 IN ( questionid, secondquestionid ) THEN 2
WHEN 11 IN ( questionid, secondquestionid ) THEN 3
END
If your RDBMS supports the VALUES constructor
SELECT questionid, secondquestionid
FROM (VALUES(6,1),
(5, 2),
(10, 3),
(11, 4)) V(q, ord)
JOIN customquestionbank
ON q IN ( questionid, secondquestionid )
GROUP BY questionid, secondquestionid
ORDER BY MIN(ord)
if your RDBMS is mysql, I think you can try ORDER BY FIELD like this:
SELECT *
FROM customquestionbank
WHERE
questionid IN(6, 5, 10, 11)
OR secondquestionid IN(6, 5, 10, 11)
ORDER BY FIELD(questionid, 6) ASC
Remove the super select that you are using
Write the query as
Select * from query1 union
Select * from query2 union
Select * from query3;
This will bring your result as needed
Else try this
Select col1,col2 from( Select 1,q1.* from query1 q1 union
Select 2,q2.* from query2 q2 union
Select 3,q1.* from query3 q3)
select only the required columns in the super query
Replace Union with Union ALL
create table #t
(
id int
)
insert into #t(id)values(5)
insert into #t(id)values(2)
insert into #t(id)values(4)
insert into #t(id)values(3)
Select * from
(
Select * from #t where id = 5
uNION All
Select * from #t where id = 2
uNION All
Select * from #t where id = 4
uNION All
Select * from #t where id = 3
)K
DROP TABLE #t

SQL to get an ordinal index in selection for specific ID

I'm working with the SQL Server database. Say, I have a selection that returns an array of IDs. Can I find out (with SQL) the order of an ID equalled N in that array?
Here's what I mean. This is my actual selection:
SELECT [id] FROM [dbo.test_db_002] t1
LEFT JOIN [dbo.test_db_003] t2
ON t1.[id]=t2.[itmid]
ORDER BY t2.[iid] ASC;
Say, it will return the following array of IDs:
13, 7, 34, 5, 2, 14
And, say I need to know what index is ID=5 in that array (the answer is 3, if 13 is index 0, 7 = index 1, 34 = index 2, 5 = becomes index 3.)
Try using ROW_NUMBER():
SELECT ROW_NUMBER() OVER (ORDER BY t2.[iid] ASC) -1 AS RowIndex
,[id]
FROM [dbo.test_db_002] t1
LEFT JOIN [dbo.test_db_003] t2
ON t1.[id]=t2.[itmid]
ORDER BY t2.[iid] ASC;
More on it here http://msdn.microsoft.com/en-us/library/ms186734.aspx
Try this:
SELECT x.RowNum - 1
FROM
(
SELECT [id], ROW_NUMBER() OVER (ORDER BY t2.[iid]) as RowNum
FROM [dbo.test_db_002] t1
LEFT JOIN [dbo.test_db_003] t2 ON t1.[id]=t2.[itmid]
) x
WHERE x.[id] = 5
Note that the -1 is because ROW_NUMBER() starts at 1 instead of 0 and you specifically mentioned a zero-indexed array.
Try this... Though I'm not sure on your table structure
Declare #Temp table
(
id int,
Name varchar(20)
)
Insert into #Temp
select 1, 'Bob'
union all
select 2, 'Mark'
union all
select 3, 'Shaun'
union all
select 4, 'Ryan'
union all
select 5, 'Steve'
union all
select 6, 'Bryan'
union all
select 7, 'Henry'
Declare #Temp2 table
(
iid int,
itmid int,
Name varchar(20)
)
Insert into #Temp2
select 1, 3, 'Thing'
union all
select 2, 2, 'This'
union all
select 3, 5, 'That'
union all
select 4, 1, 'They'
union all
select 5, 3, 'There'
union all
select 6, 5, 'Though'
union all
select 7, 6, 'Thought'
SELECT t1.[id], Row_Number() OVER (Order by t1.[id]) as RowNum
FROM #Temp t1
LEFT JOIN #Temp2 t2 ON t1.[id]=t2.[itmid]
ORDER BY t1.[id] ASC;