Tricky SQLite query, could use some assistance - sql

I have a rather confusing SQLite query that I can't seem to quite wrap my brain around.
I have the following four tables:
Table "S"
sID (string/guid) | sNum (integer)
-----------------------------------
aaa-aaa 1
bbb-bbb 2
ccc-ccc 3
ddd-ddd 4
eee-eee 5
fff-fff 6
ggg-ggg 7
Table "T"
tID (string/guid) | ... other stuff
-----------------------------------
000
www
xxx
yyy
zzz
Table "S2TMap"
sID | tID
-------------------
aaa-aaa 000
bbb-bbb 000
ccc-ccc xxx
ddd-ddd yyy
eee-eee www
fff-fff 000
ggg-ggg 000
Table "temp"
oldID (string/guid) | newID (string/guid)
------------------------------------------
dont care fff-fff
dont care ggg-ggg
dont care zzz
What I need is to be able to get the MAX() sNum that exists in a specified "t" if the sID doesn't exist in the temp.NewID table.
For example, given the T '000', '000' has S 'aaa-aaa', 'bbb-bbb', 'fff-fff', and 'ggg-ggg' mapped to it. However, both 'fff-fff' and 'ggg-ggg' exist in the TEMP table, which means I need to only look at 'aaa-aaa' and 'bbb-bbb'. Thus, the statement would return "2".
How would I go about doing this?
I was thinking something along the lines of the following for selecting s that don't exist in the "temp" table, but I'm not sure how to get the max of the seat and only do it based on a specific 't'
SELECT s.sID, s.sNum FROM s WHERE NOT EXISTS ( SELECT newID from temp where tmp.newID = s.sID)
Thanks!

Give this a try:
select max(s.sNum) result from s2tmap st
join s on st.sId = s.sId
where st.tId = '000' and not exists (
select * from temp
where temp.newId = st.sId)
Here is the fiddle to play with.
Another option, probably less efficient would be:
select max(s.sNum) result from s2tmap st
join s on st.sId = s.sId
where st.tId = '000' and st.sId not in (
select newId from temp)

The following query should give you a list of Ts and their max sNums (as long as all exist in S and S2TMap):
SELECT t.tID, MAX(sNum)
FROM S s
JOIN S2TMap map on s.sID=map.sID
JOIN T t on map.tId=t.tID
LEFT JOIN temp tmp on s.sID=tmp.newID
WHERE tmp.newID IS NULL

You were close, you just had to join on S2TMap and then to T in order to restrict the result set to a given T.
SELECT MAX(s.sNum)
FROM s
INNER JOIN S2TMap m on m.sID = s.sID
INNER JOIN t on t.tID = m.tID
WHERE t.tID = '000'
AND NOT EXISTS (
SELECT newID FROM temp WHERE temp.newID = s.sID
)

Related

How to compare and insert different tables in sql?

I have two different databases, the same column.I want to copy from the old database to the new database by comparing the student ID numbers between the tables and having the same number.
For example:
Database name 1: StudentInformation, table name: Student
StudentID Image
---------------------
123 1.png
142 2.png
175 3.png
475 4.png
Database name 2: StudentInformation2, table name: NewStudent
StudentID Image
--------------------------
145 a14.png
196 7.png
175 Null
875 9.png
475 Null
The two tables have common IDs (ID=175 and ID=475)
I want to get this table as a result:
New StudentInformation3, table name: NewSameStudents
StudentID Image
----------------------
175 3.png
475 4.png
You should try using the Merge Statement of SQL Server
Merge Statement Here
Here is one approach:
SELECT s1.StudentID, s1.Image
FROM StudentInformation.[schema].student s1
WHERE EXISTS (SELECT 1
FROM StudentInformation2.[schema].NewStudent s2
WHERE s1.StudentID = s2.StudentID
);
Demo
If you want to populate a new table using the results from the above select, then you may use the INSERT INTO ... SELECT construct:
INSERT INTO StudentInformation3.[schema].NewSameStudents (StudentID, Image)
SELECT s1.StudentID, s1.Image
FROM StudentInformation.[schema].student s1
WHERE EXISTS (SELECT 1
FROM StudentInformation2.[schema].NewStudent s2
WHERE s1.StudentID = s2.StudentID
);
But, I might recommend against this, since you can just create a (non materialized) view using the first query. Also, NewSameStudents is a derived table, and therefore might have to updated frequently, which could be a hassle.
Use inner join and isert into select statement for inserting in new table
insert into NewSameStudents(studentid,image)
select a.studentid,a.image from Student a inner join newstudent n
on a.studentid=n.studentid
where n.image is null
Below is the fully qualified SQL Query where it allows to select data between databases and even between different database servers.
SELECT * FROM [SERVER].[DATABASE].[SCHEMA].[TABLE]
SELECT
a.StudentID,
b.Image
FROM
StudentInformation.Student a
JOIN
StudentInformation2.Student b
ON a.StudentID = b.StudentID
Your 3rd table make no sense if you mean
New StudentInformation3, table name: NewSameStudents
StudentID Image
----------------------
175 3.png
475 4.png
You can use inner join
SELECT StudentID,Image
FROM StudentInformation1
INNER JOIN StudentInformation2
ON StudentInformation1.StudentID = StudentInformation1.StudentID;
try this:
WITH StudentInformation AS (
SELECT 123 AS StudentID, '1.png' AS Image UNION ALL
SELECT 142, '2.png' UNION ALL
SELECT 175, '3.png' UNION ALL
SELECT 475, '4.png'
),
StudentInformation2 AS (
SELECT 145 AS StudentID, 'a14.png' AS Image UNION ALL
SELECT 196, '7.png' UNION ALL
SELECT 175, NULL UNION ALL
SELECT 875, '9.png' UNION ALL
SELECT 475, NULL
)
SELECT s1.StudentID, s1.Image
FROM StudentInformation2 s2 left join
StudentInformation s1 ON s1.StudentID = S2.StudentID
Use Merge From SQL:
Merge into [TABLE_TARGET] as u
USING (Select * from TABLE) as c
on
u.FIELD1 = c.FIELD2 ---JOIN
--------------------
WHEN MATCHED THEN
-------------------
Update SET
FIELD_FROM_TARGET = c.FIELD_FROM_TABLE
----------------------
WHEN NOT MATCHED THEN
----------------------
Insert (
FIELD_FROM_TARGET
) VALUES
(
FIELD_FROM_TABLE
);

T-SQL cursor or if or case when

I have this table:
Table_NAME_A:
quotid itration QStatus
--------------------------------
5329 1 Assigned
5329 2 Inreview
5329 3 sold
4329 1 sold
4329 2 sold
3214 1 assigned
3214 2 Inreview
Result output should look like this:
quotid itration QStatus
------------------------------
5329 3 sold
4329 2 sold
3214 2 Inreview
T-SQL query, so basically I want the data within "sold" status if not there then "inreview" if not there then "assigned" and also at the same time if "sold" or "inreview" or "assigned" has multiple iteration then i want the highest "iteration".
Please help me, thanks in advance :)
This is a prioritization query. One way to do this is with successive comparisons in a union all:
select a.*
from table_a a
where quote_status = 'sold'
union all
select a.*
from table_a a
where quote_status = 'Inreview' and
not exists (select 1 from table_a a2 where a2.quoteid = a.quoteid and a2.quotestatus = 'sold')
union all
select a.*
from table_a a
where quote_status = 'assigned' and
not exists (select 1
from table_a a2
where a2.quoteid = a.quoteid and a2.quotestatus in ('sold', 'Inreview')
);
For performance on a larger set of data, you would want an index on table_a(quoteid, quotestatus).
You want neither cursors nor if/then for this. Instead, you'll use a series of self-joins to get these results. I'll also use a CTE to simplify getting the max iteration at each step:
with StatusIterations As
(
SELECT quotID, MAX(itration) Iteration, QStatus
FROM table_NAME_A
GROUP BY quotID, QStats
)
select q.quotID, coalesce(sold.Iteration,rev.Iteration,asngd.Iteration) Iteration,
coalesce(sold.QStatus, rev.QStatus, asngd.QStatus) QStatus
from
--initial pass for list of quotes, to ensure every quote is included in the results
(select distinct quotID from table_NAME_A) q
--one additional pass for each possible status
left join StatusIterations sold on sold.quotID = q.quotID and sold.QStatus = 'sold'
left join StatusIterations rev on rev.quotID = q.quotID and rev.QStatus = 'Inreview'
left join StatusIterations asngd on asngd.quotID = q.quotID and asngd.QStatus = 'assigned'
If you have a table that equates a status with a numeric value, you can further improve on this:
Table: Status
QStatus Sequence
'Sold' 3
'Inreview' 2
'Assigned' 1
And the code becomes:
select t.quotID, MAX(t.itration) itration, t.QStatus
from
(
select t.quotID, MAX(s.Sequence) As Sequence
from table_NAME_A t
inner join Status s on s.QStatus = t.QStatus
group by t.quotID
) seq
inner join Status s on s.Sequence = seq.Sequence
inner join table_NAME_A t on t.quotID = seq.quotID and t.QStatus = s.QStatus
group by t.quoteID, t.QStatus
The above may look like complicated at first, but it can be faster and it will scale easily beyond three statuses without changing the code.

Select random record within groups sqlite

I can't seem to get my head around this. I have a single table in SQlite, from which I need to select a random() record for EACH group. So, considering a table such as:
id link chunk
2 a me1
3 b me1
4 c me1
5 d you2
6 e you2
7 f you2
I need sql that will return a random link value for each chunk. So one time I run it would give:
me1 | a
you2 | f
the next time maybe
me1 | c
you2 | d
I know similar questions have been answered but I'm not finding a derivation of one that applies here.
UPDATE:
Nuts, follow up question: so now I need to EXCLUDE rows where a new field "qcinfo" is set to 'Y'.
This, of course, hides rows whenever the random ID hits one where qcinfo = 'Y', which is wrong. I need to exclude the row from being considered in the chunk, but still generate a random record for the chunk if any records have qcinfo <> 'Y'.
select t.chunk ,t.id, t.qcinfo, t.link from table1
inner join
(
select chunk ,cast(min(id)+abs(random() % (max(id)-min(id)))as int) AS random_id
from table1
group by chunk
) sq
on t.chunk = sq.chunk
and t.id = sq.random_id
where qcinfo <> 'Y'
A bit hackish, but it works... See sql fiddle http://sqlfiddle.com/#!2/81e75/7
select t.chunk
,t.link
from table1 t
inner join
(
select chunk
,FLOOR(min(id) + RAND() * (max(id)-min(id))) AS random_id
from table1
group by chunk
) sq
on t.chunk = sq.chunk
and t.id = sq.random_id
Sorry, I thought that you said MySQL.
Here is the fiddle and the code for SQLite
http://sqlfiddle.com/#!5/81e75/12
select t.chunk
,t.link
from table1 t
inner join
(
select chunk
,cast(min(id)+abs(random() % (max(id)-min(id)))as int) AS random_id
from table1
group by chunk
) sq
on t.chunk = sq.chunk
and t.id = sq.random_id
Note that SQLite returns the first value corresponding to a group when we do a group by without an aggregation
select link, chunk from table group by chunk;
Running that you'll get this
me1 | a
you2| d
Now you can make the first value random by randomly sorting the table and then grouping. Here's the final solution.
select link, chunk from (select * from table order by random()) group by chunk;

How to do a subquery using the result obtained in the original query?

Here is the situation I have..I have to fetch all the associated cases for a given quoteId and this requires a join of 3 tables and I am able to come up with a query for that. Below is the sample : for brevity I have omitted some table name and used only Alias name.
SELECT distinct caseTable.CASEID, quoteHdrTable.Case_UID FROM
caseTable INNER JOIN quoteHdrTable ON
quoteHdrTable.Case_UID = caseTable.Case_UID WHERE quoteHdrTable.QUOTE_ID = '12345'.
Now for each CASE_UID that returns back, I also need to display its status from a different table. That has structure below.
STATUS_TABLE
CASE_UID STATUS
------------ -----------
123 Good
234 Bad.
345 {null}
In the end I want a result like
result
case_ID case_UID status
001 123 Good
Can we use subquery to do a 2nd SQL using the result(case_UID) from first..please provide pointers or a sample SQL statement.
FYI..using DB2 database
Thanks
Sandeep
SELECT distinct c.CASEID, q.Case_UID, s.status
FROM caseTable c
INNER JOIN quoteHdrTable q ON q.Case_UID = c.Case_UID
LEFT JOIN StatusTable s ON s.CASE_UID = q.CASE_UID
WHERE quoteHdrTable.QUOTE_ID = '12345'
Why not just add another JOIN?
SELECT distinct
caseTable.CASEID,
quoteHdrTable.Case_UID,
status.STATUS
FROM caseTable
INNER JOIN quoteHdrTable
ON quoteHdrTable.Case_UID = caseTable.Case_UID
INNER JOIN STATUS_TABLE status
ON quoteHdrTable.Case_UID = status.Case_UID
WHERE quoteHdrTable.QUOTE_ID = '12345'

SQL Server Query optimisation question

I have the following queries, and I would love to find a better way to do this as it doesn't seem right the way I am doing it...
EDIT
Sorry I did not specify that I only want to return adverts that actually match all amenities!
SELECT TOP 50 Advert.Id
FROM Advert
WHERE Id in(SELECT Advert_id FROM AdvertsToAmenities WHERE Amenity_id = 1 AND Advert_Id = Id)
AND Id in(SELECT Advert_id FROM AdvertsToAmenities WHERE Amenity_id = 3 AND Advert_Id = Id)
AND Id in(SELECT Advert_id FROM AdvertsToAmenities WHERE Amenity_id = 5 AND Advert_Id = Id)
-- OR --
SELECT TOP 50 Advert.Id
FROM Advert
JOIN AdvertsToAmenities a on Advert.Id = a.Advert_id
JOIN AdvertsToAmenities b on Advert.Id = b.Advert_id
JOIN AdvertsToAmenities c on Advert.Id = c.Advert_id
WHERE a.Amenity_id = 1
AND b.Amenity_id = 3
AND c.Amenity_id = 5
I would love to find out how to optimize these queries!
Your queries look fine to me. Another alternative would be to use something like this:
SELECT TOP 50 Advert.Id
FROM Advert JOIN AdvertsToAmenities a ON Advert.Id = a.Advert_id
WHERE a.Amenity_id = 1
OR a.Amenity_id = 3
OR a.Amenity_id = 5
GROUP BY Advert.Id
HAVING COUNT(DISTINCT a.Amenity_id) = 3
If the (Advert_Id, Amenity_id) pairs are unique, you can drop the DISTINCT.
How about the following restructure of your query which allows you to simply create a comma seperated list of specific amenity IDs?
SELECT TOP 50
a.[ID]
FROM [Advert] a
INNER JOIN
(
SELECT ata.[Advert_ID]
FROM [AdvertsToAmenities] ata
WHERE ata.[Amenity_ID]
IN (1, 3, 4, 5) -- Your list of amenity IDs
) as ata
ON a.[ID] = ata.[Advert_ID]
This uses a dynamic table statement to prefilter only those applicable amenities before joining back against the list of adverts. Note, Sql Server will heavily optimise the sub query before execution. This should mean that you'll get the benefit of a more maintainable statement with all the optimisations that Sql Server provides.
* Try this simple *
SELECT TOP 50 a.Id
FROM Advert a, AdvertsToAmenities b
WHERE a.Amenity_id in ('1','3','5')
and a.Id = b.Amenity_id