(TRANSACT SQL) how to create Master-Detail in a row using sql? - sql

I have two tables
table1
------
ID
NAME
ADDRESS
table2
-------
ID
PHONE
EMAIL
how can i create report like this
------------------------------------
01 Dave 123 Veneu
555-5 A#YAHOO.COM
66-66 B#Yahoo.co.id
213-1 D#c.com
02 John 23 Park
322-1 C#you.com
54-23 D#Net.com
231-2 me#you.com
im using sql server 2005 express,, thank you in advance.

Not sure why you would ever want to write this in anything other than a report designer, but just for the hell of it:
SELECT ID AS Column1, NAME AS Column2, Address AS Column3, ID AS SortColumn1, 1 AS SortColumn2
UNION
SELECT '', PHONE, EMAIL, ID AS SortColumn1, 2 AS SortColumn2
ORDER BY SortColumn1, SortColumn2
The output is going to basically be a load of gibberish really, and you've got the two extra columns on the end of to get rid of.

You shouldn't. It's a general principle that formatting should not be done in the database layer.
SQL Server should be used to generate data, then your application should process the data, including the formatting.
I would open two queries. One that loads table one, ordered by the ID column. And the other that load table two, also ordered by the ID column. You can then iterate through both record sets at the same time, something like the following pseudo-code...
rs1 = SQL.Execute("SELECT * FROM table1 ORDER BY ID")
rs2 = SQL.Execute("SELECT * FROM table2 ORDER BY ID, phone")
rs2.Next()
WHILE rs1.Next()
Output The Address Info Here
WHILE rs1.ID = rs2.ID
Output The Phone/Email Info Here
rs2.Next()
END WHILE
END WHILE

In order to have one-to-many relationship, as in your example (one person has multiple phones and emails), you need to add some kind of link column to the second table, which would contain the ID of the person the email / phone belongs to.
So your table structure should look like this:
table1
------
ID
NAME
ADDRESS
table2
-------
ID
TABLE1_ID
PHONE
EMAIL
Then, you could query your data using joins:
SELECT table1.name, table1.address, table2.phone, table2.email WHERE table2.table1_id = table1.id

I strongly agree with dems, but if you really need to come up with something like that, the following could work (albeit without the empty lines)
SELECT case
when group_rn = 1 then id
else ''
end as id,
case
when group_rn = 1 then name
else phone
end as name_phone_column,
case
when group_rn = 1 then address
else _email
end as address_email_column
FROM (
SELECT t1.id,
t1.name,
t1.address,
t2.email,
t2.phone,
row_number() over (partition by t1.id order t1.name) as group_rn
FROM table1 t1
LEFT JOIN table2 t2 ON t1.id = t2.id
) t
ORDER BY id
This assumes that phone and name both have the same datatype, just like address and email.

Related

SQL - How to get values from multiple tables without being ambiguous

Apologies if this question had been asked before (it probably did). I never used SQL before and the answers I've got only got me more confused.
I need to find out if an ID exists on different tables and get the total number from all tables.
Here is my query:
select * from public.ui1, public.ui2, public.ui3 where id = '123'
So if id 123 doesn't exist in ui1 and ui2 but does exist in ui3, I'd still like to get it. (I would obviously like to get it if it exists in the other tables)
I am currently getting an ambiguous error message as id exists in all tables but I am not sure how to construct this query in the appropriate manner. I tried join but failed miserably. Any help on how to reconstruct it and a stupid proof explanation would be highly appreciated!
EDIT: What I would finally like to find out is if id = 123 exists in any of the tables.
It's a bit unclear what the result is you expect. If you want the count then you can use a UNION ALL
select 'ui1' as source_table,
count(*) as num_rows
from public.ui1
where id = 123
union all
select 'ui2',
count(*)
from public.ui2
where id = 123
union all
select 'ui3',
count(*)
from public.ui3
where id = 123
If you only want to know if the id exists in at least one of the tables (so a true/false) result you can use:
select exists (select id from ui1 where id = 123
union all
select id from ui2 where id = 123
union all
select id from ui3 where id = 123)
What I would finally like to find out is if id = 123 exists in any of the tables.
The best way to do this is probably just using exists:
select v.id,
(exists (select 1 from public.ui1 t where t.id = v.id) or
exists (select 1 from public.ui2 t where t.id = v.id) or
exists (select 1 from public.ui3 t where t.id = v.id)
) as exists_flag
from (values (123)) v(id);
As written, this returns one row per id defined in values(), along with a flag of whether or not the id exists -- the question you are asking.
This can easily be tweaked if you want additional information, such as which tables the id exists in, or the number of times each appears.

SQL - Removing result of one table based on the tree structure of another

To better define the question I'm asking, it'd probably be easier to introduce you to the data I'm working with.
Essentially I have two tables joined that kind of look like this:
Table 1
Product ID AccountLinkID (FK)
PRODUCT00001 AC000001
PRODUCT00001 AC000002
PRODUCT00001 AC000003
PRODUCT00001 AC000004
Table 2
Link (FK) AccountType
AC000001 1
AC000002 2
AC000003 3
AC000004 4
As part of some data i'm looking at, I want to make sure that if any ProductID is linked to an account type '4' that the product ID is removed from the search.
The problem Is that the foreign key isn't also a single number - as one product can be linked to multiple account types (for example, one produce could be linked to a sellers account, buyers account, customer account etc)
So in this instance - account type 4 is something like a 'dummy' account, therefore any productID's linked to it aren't ID's I want including in the search.
I can't think of how to use the account type as a means to remove the product id.
Thank you in advance for any advice.
If you want just one row per productid, you can join, aggregate, and filter out with a having clause
select t1.productid
from table1 t1
inner join table2 t2 on t2.link = t1.accountlinkid
group by t1.productid
having max(case when t2.accounttype = 4 then 1 else 0 end) = 0
If, on the other hand, you want entire rows from t1, then window functions are a better option:
select t.*
from (
select t1.*,
max(case when t2.accounttype = 4 then 1 else 0 end) over(partition by t1.productid) has_type4
from table1 t1
inner join table2 t2 on t2.link = t1.accountlinkid
) t
where has_type4 = 0

Nested SQL Queries with Self JOIN - How to filter rows OUT

I have an SQLite3 database with a table upon which I need to filter by several factors. Once such factor is to filter our rows based on the content of other rows within the same table.
From what I've researched, a self JOIN is going to be required, but I am not sure how I would do that to filter the table by several factors.
Here is a sample table of the data:
Name Part # Status Amount
---------------------------------
Item 1 12345 New $100.00
Item 2 12345 New $15.00
Item 3 35864 Old $132.56
Item 4 12345 Old $15.00
What I need to do is find any Items that have the same Part #, one of them has an "Old" Status and the Amount is the same.
So, first we would get all rows with Part # "12345," and then check if any of the rows have an "Old" status with a matching Amount. In this example, we would have Item2 and Item4 as a result.
What now would need to be done is to return the REST of the rows within the table, that have a "New" Status, essentially discarding those two items.
Desired Output:
Name Part # Status Amount
---------------------------------
Item 1 12345 New $100.00
Removed all "Old" status rows and any "New" that had a matching "Part #" and "Amount" with an "Old" status. (I'm sorry, I know that's very confusing, hence my need for help).
I have looked into the following resources to try and figure this out on my own, but there are so many levels that I am getting confused.
Self-join of a subquery
ZenTut
Compare rows and columns of same table
The first two links dealt with comparing columns within the same table. The third one does seem to be a pretty similar question, but does not have a readable answer (for me, anyway).
I do Java development as well and it would be fairly simple to do this there, but I am hoping for a single SQL query (nested), if possible.
The "not exists" statment should do the trick :
select * from table t1
where t1.Status = 'New'
and not exists (select * from table t2
where t2.Status = 'Old'
and t2.Part = t1.Part
and t2.Amount = t1.Amount);
This is a T-SQL answer. Hope it is translatable. If you have a big data set for matches you might change the not in to !Exists.
select *
from table
where Name not in(
select Name
from table t1
join table t2
on t1.PartNumber = t2.PartNumber
AND t1.Status='New'
AND t2.Status='Old'
and t1.Amount=t2.Amount)
and Status = 'New'
could be using an innner join a grouped select for get status old and not only this
select * from
my_table
INNER JOIN (
select
Part_#
, Amount
, count(distinct Status)
, sum(case when Status = 'Old' then 1 else 0 )
from my_table
group part_#, Amount,
having count(distinct Status)>1
and sum(case when Status = 'Old' then 1 else 0 ) > 0
) t on.t.part_# = my_table.part_#
and status = 'new'
and my_table.Amount <> t.Amount
Tried to understand what you want best I could...
SELECT DISTINCT yt.PartNum, yt.Status, yt.Amount
FROM YourTable yt
JOIN YourTable yt2
ON yt2.PartNum = yt.PartNum
AND yt2.Status = 'Old'
AND yt2.Amount != yt.Amount
WHERE yt.Status = 'New'
This gives everything with a new status that has an old status with a different price.

How to write a SQL-Query for the following table?

My table is defined like this:
Name is a string and property too.
ID | Name | Property
An example for data in this table is this:
ID | Name | Property
1 Peter Newsletter
2 Paul Register
3 Peter Register
4 Shaun Newsletter
5 Steve Register
Now I like to query all people that have the property newsletter and register.
As a result I should get Peter, because he has both property's.
So the resulting table should be like:
ID | Name | Property
1 Peter Newsletter
3 Peter Register
So everything I try to query is which person has both property's newsletter and register.
Here is one method:
select t.*
from table t
where exists (select 1
from table t2
where t2.name = t.name and t2.property = 'NewsLetter'
) and
exists (select 1
from table t2
where t2.name = t.name and t2.property = 'Register'
);
If you just want the list of names, perhaps with ids, I would do that as:
select t.name
from table t
where t2.property in ('NewsLetter', 'Register')
group by t.name
having count(distinct property) = 2;
How you get the list of ids depends on your database, something like listagg() or group_concat() or string_agg().
An alternative, pretty much on the same lines as Gordon's solution, but without using EXISTS:
select * from tablename
where name in (select name from tablename where property = 'Newsletter')
and name in (select name from tablename where property = 'Register')
One more way:
SELECT * FROM T as T1
WHERE Property IN ('Newsletter','Register')
AND EXISTS (SELECT * FROM T
WHERE Name=T1.Name
and Property IN ('Newsletter','Register')
and Property <> T1.Property
)
SQLFiddle demo
Another one, for the record
WITH cteHasBoth
as (select Name
from MyTable
where Property in ('Newsletter', 'Register')
group by Name
having count(*) = 2)
select ID, Name
from MyTable
where name in (select Name from cteHasBoth)
This would require only two sacns through the table.
It's hard to be sure without knowing more about the data. Given the exact requirements that you gave to us, this will give the results you showed:
WITH multprop (multName) AS (
SELECT NAME FROM myTable
WHERE Property IN('Newsletter','Register')
GROUP BY NAME
HAVING count(*)>1 )
select id, Name, Property
from multprop inner join myTable
on multName = Name
But minor differences in your requirements will mess things up. For example, will there ever be Property values other than the two you listed? Or can a Name show up multiple times with the same Property?
EDIT: The added WHERE clause limits rows in the CTE to the requested specific set of Property values. This is from the more detailed requirements in comments.

How to select most populated record?

I have the unfortunate luck of having to deal with a db that contains duplicates of particular records, I am looking for a quick way to say "get the most populated record and update the duplicates to match it".
From there I can select distinct records and get a useful set of records.
Any ideas?
It's mainly names and addresses if that helps...
Ok lots of questions asked here so i'll add little bit more:
Firstly I want to pull the most "populated" not most "popular", this means the row with the most values that are not null.
Once I have the set (which is easy because in my case the id's match) I can then populate the missing values in the other rows.
I don't want to destroy data and i only intend to update data based on an accurate match (eg by id).
My problem at the moment is figuring out which of a set of rows has the most populated fields, having said that since posting this question I have found a different way to solve my bigger problem which is what to send to a remote server however I'm still interested to know what the solution to this might be.
Sample data might look something like this ...
id name addr1 addr2 ect
1 fred 1 the street Some town ...
1 fred null null null
Given a table full of matching pairs like this I want to find the pairs then grab the one with the info in it and insert those values where there is a null in the other row.
Keep in mind that you will be potentially destroying data here. Just because a row has fewer columns filled doesn't mean that it's less accurate in the columns that are filled.
I've assumed that duplicates are determined by a column called "name". You'll need to adjust based on your definition of duplicates. Also, since you didn't give any rules on how to deal with ties for "most populated" I just chose the row with the lowest id.
UPDATE
T1
SET
col_1 = T2.col_1,
col_2 = T2.col_2,
....
FROM
My_Table T1
INNER JOIN My_Table T2 ON
T2.name = T1.name AND
T2.id =
(
SELECT TOP 1
T3.id
FROM
My_Table T3
WHERE
T3.name = T1.name
ORDER BY
CASE WHEN col_1 IS NOT NULL THEN 1 ELSE 0 END +
CASE WHEN col_2 IS NOT NULL THEN 1 ELSE 0 END +
... DESC,
id ASC
)
EDIT: I just reread your question and you mention, "From there I can select distinct records and get a useful set of records." If that's what you really want, then don't bother updating the other rows, just select the ones that you want in the first place and leave everything else intact:
SELECT
T1.id,
T1.name,
T1.col_1,
T1.col_2,
...
FROM
My_Table T1
WHERE
T1.id =
(
SELECT TOP 1
T2.id
FROM
My_Table T2
WHERE
T2.name = T1.name
ORDER BY
CASE WHEN T2.col_1 IS NOT NULL THEN 1 ELSE 0 END +
CASE WHEN T2.col_2 IS NOT NULL THEN 1 ELSE 0 END +
... DESC,
T2.id ASC
)