Multiple selects really needed? - sql

I have the following table.
____________________________________
| carid | changeid | data1 | data2 |
|_______|__________|_______|_______|
| 1 | 1 |a |b |
| 1 | 2 |c |d |
| 1 | 3 |e |f |
| 2 | 3 |g |h |
| 2 | 2 |i |j |
| 2 | 4 |k |l |
| 3 | 5 |m |n |
| 3 | 1 |o |p |
| 4 | 6 |q |r |
| 4 | 2 |s |t |
|_______|__________|_______|_______|
I want to select the following result:
| carid | changeid | data1 | data2 |
|_______|__________|_______|_______|
| 1 | 1 |a |b |
| 1 | 2 |c |d |
| 1 | 3 |e |f |
| 3 | 5 |m |n |
| 3 | 1 |o |p |
|_______|__________|_______|_______|
In words:
If a row has changeid=1 I want to select all the rows with the same carid as the row with changeid=1.
This problem is quite easy to solve with a query using multiple selects. First select all rows with changeid=1 and take those carids and select all rows with those carids. Simple enough.
I was more wondering if it is possible to solve this problem without using multiple selects? Preferably I'm looking for a faster solution but I can try that out myself.

You can join the table back to itself
SELECT DISTINCT a.*
FROM YourTable a
INNER JOIN YourTable b ON b.carid = a.carid and b.changeid = 1
Table a is all the rows you want to output, filtered by table b which limits the set to those with changeid = 1.
This should have excellent performance as everything is done in a set oriented manner.
DISTINCT may not be necessary if changeid 1 may only occur once, and should be avoided if possible as it may introduce a significant performance hit for a large result set.

For multiple select you mean using IN?
SELECT carid, changeid, data1, data2
FROM YourTable
WHERE carid IN (SELECT carid FROM YourTable WHERE changeid = 1)

Most databases support window functions. You can do this as:
select carid, changeid
from (select t.*,
max(case when changeid = 1 then 1 else 0 end) over
(partition by carid) as HasChangeId1
from YourTable t
) t
where HasChangeId1 = 1;
If the "1" is the minimum value for the change id, this can be simplified to:
select carid, changeid
from (select t.*,
min(changeid) over (partition by carid) as MinChangeId
from YourTable t
) t
where MinChangeId = 1;

It sounds like you're after only the combinations of carid and changeid present in the table, in which case the DISTINCT will return only the unique combinations for you. Not sure if that is what you're after but give it a go and check it for your expected behaviour...
SELECT DISTINCT CARID, CHANGEID FROM UnknownTable

Related

In SQL, how can I show the first row of a group and hide one column in the rest of the group

I am trying to export an SQL query to Excel, and I need this behaviour but I can't find in anywhere.
This is what I currently have:
SELECT t1.c1, t2.c2
FROM table1 t1
INNER JOIN table2 t2 ON t1.id = t2.id;
|c1| c2 |
----------------
|a | sometext1 |
|a | sometext2 |
|a | sometext3 |
|a | sometext4 |
|b | sometext5 |
|b | sometext6 |
|b | sometext7 |
I want to show the results like this:
|c1| c2 |
|a | sometext1 |
| | sometext2 |
| | sometext3 |
| | sometext4 |
|b | sometext5 |
| | sometext6 |
| | sometext7 |
I want to show only the first item in each group, and hide the rest so it's not shown in Excel.
I am using SQL Server.
You should really do this type of processing in the application, not the database. Why? Because result sets represent unordered sets. Relying on the ordering to understand data makes the results brittle.
But you can. One method is:
select (case when seqnum = 1 then c1 end) as c1, c2
from (select, t.*,
row_number() over (partition by c1 order by (select null)) as seqnum
from t
) t
order by c1, seqnum;

selecting one duplicate from re-occurances with only one varying colum SQL

Current State
id | val | varchar_id| uid
----------------------
1 | 1 | A4D NEWID()
1 | 2 | A3G NEWID()
2 | 1 | 7S3 NEWID()
2 | 1 | 43E NEWID()
2 | 2 | 7S3 NEWID()
2 | 2 | 431 NEWID()
3 | 1 | 432 NEWID()
3 | 2 | 43P NEWID()
Ideal state
id | val | varchar_id|
----------------------
1 | 1 | A4D NEWID()
1 | 2 | A3G NEWID()
2 | 1 | 7S3 NEWID()
2 | 2 | 7S3 NEWID()
3 | 1 | 432 NEWID()
3 | 2 | 43P NEWID()
Removing of duplicate occurrences of id + val
I have tried (pseudo code below):
SELECT *
from table
WHERE uid = MAX
GROUP BY id, val
Does anyone know of a solution to this/ am I missing something here? I do not mind which of the duplicates are returned.
Also, the version of Sybase I am using does not allow Partition x over x,y functionality.
Using SQL you can do it this way. Also your where clause isn't what SQL supports.
DECLARE #T TABLE (ID INT, Val INT, V_ID VARCHAR(50), uidd UNIQUEIDENTIFIER)
INSERT INTO #T VALUES
(1,1,'A4D',NEWID()),
(1,2,'A3G',NEWID()),
(2,1,'7S3',NEWID()),
(2,2,'43E',NEWID()),
(2,2,'7S3',NEWID()),
(2,2,'431',NEWID()),
(3,1,'432',NEWID()),
(3,2,'43P',NEWID())
SELECT t.id, t.Val, MAX(V_ID) AS varchar_id, MAX(uidd)
FROM #T AS t
GROUP BY id, val
ORDER BY id, val
This will give you the result
+---+----+-----------+-------------------------------------+
|id |Val |varchar_id |uid |
+---+----+-----------+-------------------------------------+
|1 |1 |A4D |5296ACE4-573A-4A7E-882F-516EA8E9DBDD |
|1 |2 |A3G |3EE82BEE-8C18-4415-BB3D-110F443409B5 |
|2 |1 |7S3 |68DBF7B3-316D-4A8B-B8AD-8825EC83585D |
|2 |2 |7S3 |01C54277-7156-47E1-9205-DD577A726196 |
|3 |1 |432 |6F53F332-FC9C-4EE1-A3D2-1D0FD002DDAF |
|3 |2 |43P |7B532EBD-E6C9-4BE4-B0F7-FCBCB9CE1D61 |
+---+----+-----------+-------------------------------------+

SQL query to select rows that based on some value of other rows in the same table

Assume I have the following activity table:
id type value flag
------|------|-------|------|
1 |A | 13 | 1 |
2 |B | 29 | |
3 |C | 11 | |
4 |A | 78 | |
5 |X | 91 | |
6 |C | 2 | |
7 |B | 14 | 1 |
I want to select rows that any row with the same type has the flag 1 or the type is X. In this case, I would like to get:
id type value flag
------|------|-------|------|
1 |A | 13 | 1 |
4 |A | 78 | |
2 |B | 29 | |
7 |B | 14 | 1 |
5 |X | 91 | |
I can do an INNER JOIN to get the result like:
SELECT
"activities".*
FROM "activities"
INNER JOIN activities AS a2
ON activities.type = a2.type
AND a2.flag = 1
OR activities.type = 'X'
AND activities.id = a2.id
However, this is slow when the amount of records becomes large, especially when I need to do a COUNT(*) on top of the result. I wonder how I can rewrite the query and make it more performant.
I am using Postgres. Thanks!
Here are 3 ways to do it. Choose one which will be work faster on your dataset. To speed up these queries you have to create indexes on type and flag fields.
select a.* from activities a
JOIN (select distinct type FROM activities where type='X' or flag=1) t
ON a.type=t.type;
select a.* from activities a
where type='X'
or EXISTS(SELECT * FROM activities WHERE type=a.type AND flag=1);
select a.* from activities a
where type='X'
or type IN (SELECT type FROM activities WHERE flag=1)
SQLFiddle demo
Try this solution
SELECT id,
type,
value,
flag
FROM (SELECT *,
SUM(CASE WHEN flag = 1 THEN 1 ELSE 0 END) OVER (PARTITION BY type) AS flag_occurence_for_type
FROM activities) t
WHERE type = 'X' OR flag_occurence_for_type > 0

On MS Access, group by then filter

I have a table on ms access which has 13 columns.I want to group by column Name then check the latest one by comparing column id and take the record if the latest row has value if not take the previous record. the comparison will be done for each columns.
+-----+-----+-------+-------+-------+
| id |Name |colum1 |colum2 |colum3 |
+-----+-----+-------+-------+-------+
| 1 |a |x | |x |
+-----+-----+-------+-------+-------+
| 2 |b | |y |y |
+-----+-----+-------+-------+-------+
| 3 |a |z |z | |
+-----+-----+-------+-------+-------+
| 4 |a |m | | |
+-----+-----+-------+-------+-------+
Expected output
+-----+-----+-------+-------+-------+
| id |Name |colum1 |colum2 |colum3 |
+-----+-----+-------+-------+-------+
| 2 |b | |y |y |
+-----+-----+-------+-------+-------+
| 4 |a |m |z |x |
+-----+-----+-------+-------+-------+
You can do Self Join.
SELECT T1.*
FROM
table_name T1
INNER JOIN
(SELECT `Name`,MAX(`id`) AS ID FROM table_name GROUP BY `Name` ) T2
ON T1.`id`= T2.`ID` AND T1.`Name` = T2.`Name`
Hope this helps.
I'm not sure if it will work in MS Access. It works in SQL Server. Even if it does, it will be very slow.
SELECT
Groups.Name
,(
SELECT TOP(1) T.colum1
FROM T
WHERE T.Name = Groups.Name AND T.colum1 <> ''
ORDER BY T.ID DESC
) AS C1
,(
SELECT TOP(1) T.colum2
FROM T
WHERE T.Name = Groups.Name AND T.colum2 <> ''
ORDER BY T.ID DESC
) AS C2
,(
SELECT TOP(1) T.colum3
FROM T
WHERE T.Name = Groups.Name AND T.colum3 <> ''
ORDER BY T.ID DESC
) AS C3
FROM
(
SELECT Name
FROM T
GROUP BY Name
) AS Groups

Many to one relation, select only rows which ancestor met all criteria

First I will show You architecture of tables.
Table "public.questionare"
Column | Type |
--------------------+-----------------------+
id | integer |
Table "public.questionareacceptance"
Column | Type |
-------------------------+-----------------------+
id | integer |
questionare_id | integer |
accept_id | integer |
impact_id | integer |
Table questionareacceptance contains:
id | questionare_id | accept_id| impact_id |
----+----------------+----------+------------------+
1 |1 |1 | |
2 |1 |1 | 1 |
3 |1 |1 | 1 |
4 |2 | | 1 |
5 |3 |1 | 1 |
6 |4 |1 | 1 |
7 |4 |1 | 1 |
What I am trying to get is a list of questionare ID where in each questionareacceptance fields accept_id and impact_id are not NULL
My query looks like:
SELECT q.id AS quest,
qa.id AS accepted
FROM questionare q,
questionareacceptance qa
WHERE q.id = qa.questionare_id
AND qa.accept_id IS NOT NULL
AND qa.impact_id IS NOT NULL;
But the result is as fallows:
quest | accepted |
--------------------+-----------------------+
1 |1 |
1 |2 |
1 |3 |
2 |4 |
3 |5 |
4 |6 |
4 |7 |
But the result that should be returned are only 3 and 4 others have impact_id or accept_id null.
Can anyone point me where I am doing the mistake?
your query could be written with not exists:
select
q.id as quest, qa.id as accepted
from questionare as q
inner join questionareacceptance as qa on qa.questionare_id = q.id
where
not exists (
select *
from questionareacceptance as tqa
where
tqa.questionare_id = q.id and
(tqa.accept_id is null or tqa.impact_id is null)
)
but I think faster one would using window functions:
with cte as (
select
q.id as quest, qa.id as accepted,
sum(case when qa.accept_id is not null and qa.impact_id is not null then 1 else 0 end) over(partition by q.id) as cnt1,
count(*) over(partition by q.id) as cnt2
from questionare as q
inner join questionareacceptance as qa on qa.questionare_id = q.id
)
select quest, accepted
from cte
where cnt1 = cnt2
actually looks like you don't need join at all:
with cte as (
select
qa.questionare_id as quest, qa.id as accepted,
sum(case when qa.accept_id is not null and qa.impact_id is not null then 1 else 0 end) over(partition by qa.questionare_id) as cnt1,
count(*) over(partition by qa.questionare_id) as cnt2
from questionareacceptance as qa
)
select quest, accepted
from cte
where cnt1 = cnt2;
sql fiddle demo