postgresql statement prob - sql

I have two table A and B as following.
A:
key type
0 t
1 f
2 t
3 f
4 t
5 t
.......
B:
key name
0 Mary
0 Tony
0 Krolik
1 Tom
2 Tony
3 Tony
3 Mary
3 Tom
4 Tony
4 Tim
5 Tim
5 Mary
5 Wuli
.....
I hope to find top n occurence name that it's type is 'f'.
For example, in A, the type of key 1 and 3 are 'f', we find key 1 and 3 in table B, there are 2 'Tom' and 1 'Mary' and 1 'Tony'.
1 Tom
3 Tony
3 Mary
3 Tom
if n = 1 and the table is just showed as before, we hope to get 'Tom', because its occurence is top 1.
How can I write sql statement to satisfy these requirement?
I write something like below, but it is wrong. Can anyone help me? I assume n = 20.
SELECT DISTINCT TOP 20 name
FROM B
WHERE key IN (
SELECT key
FROM A
WHERE "type" = 'f'
)
GROUP BY name
ORDER BY DESC;

You don't seem to need aggregation. And the equivalent of top in Postgres is limit or fetch first <row> rows:
SELECT name, key
FROM B
WHERE B.key IN (SELECT A.key
FROM A
WHERE "type" = 'f'
)
ORDER BY key;
This corresponds to the results presented in the question. Your description doesn't quite match those results.

Related

T-SQL - How to use results of a query to select multiple rows based on a count

Considering the below example Person table, I am looking for a way to return multiple rows based on an X value. So, if the BagsOut count is, say 3, I would want 3 copies of the row returned or if it was 2 then 2 copies should be returned, and so on:
ID Name BagsOut
1 Ken 1
2 Dave 3
3 Ben 2
Desired result:
ID Name BagsOut
1 Ken 1
2 Dave 3
2 Dave 3
2 Dave 3
3 Ben 2
3 Ben 2
Is it possible to write this into a single query? I am using T-SQL.
Many thanks in advance for looking.
you can use recursive CTE to achieve this.
declare #t table(ID int, Name char(5), BagsOut int)
insert into #t values
(1 ,'Ken', 1)
,(2 ,'Dave', 3)
,(3 ,'Ben', 2)
;with cte_bags as
(
select id, name, BagsOut,1 as currentRow from #t
union all
select id, name, BagsOut, currentRow+1
from cte_bags
where currentRow < BagsOut)
select id, name, BagsOut from cte_bags
order by id
id
name
BagsOut
1
Ken
1
2
Dave
3
2
Dave
3
2
Dave
3
3
Ben
2
3
Ben
2

Adding column to table and automatically UNION tables afterwards

I have the following problem:
I have a table with distinct pairs of ID and Indicator.
Table A:
ID Indicator
----------------------
1 1
1 2
1 3
2 1
2 2
2 3
----------------------
And a second table with distinct names.
Table B:
Names
-------
John
Lea
Peter
--------
What I want is a column in table A with the names of Table B for every ID-Indicator pair:
Outcome:
ID Indicator Names
----------------------------------
1 1 John
1 2 John
1 3 John
2 1 John
2 2 John
2 3 John
1 1 Lea
1 2 Lea
1 3 Lea
2 1 Lea
2 2 Lea
2 3 Lea
1 1 Peter
1 2 Peter
1 3 Peter
2 1 Peter
2 2 Peter
2 3 Peter
----------------------------------
In reality Table B, with the names, is really long, so a manual solution like:
SELECT ID, Indicator, Names = 'John'
FROM TableA
UNION
SELECT ID, Indicator, Names = 'Lea'
FROM TableA
Is not feasible. Is there a non-manual solution to this problem?
Any help is highly appreciated!
You want to join each row in TableA with each row in TableB, this is known as a cross join, as commented. The number of rows output will be A * B.
If you want all columns from both tables the syntax is simply
select *
from TableA cross join TableB

Why does the cte return the error that it does not exist?

Here is my code
WITH CTE AS(
SELECT COUNT(CASE name WHEN 'John' THEN 1 END) OVER (PARTITION BY BlockID ORDER BY Step) AS Johns
FROM dbo.YourTable)
DELETE FROM CTE
WHERE Johns >= 1;
SELECT *
FROM dbo.YourTable;
It returns me the following error when I run the code in the notebook
ERROR: syntax error at or near "DELETE"
But I can't seem to find any mistake in the query
When I try to do it in online compiler it returns the error that relation "cte" does not exist
Maybe this errors can be related?...
Here what I'm trying to do with cte:
My first table:
Block_id step name
1 1 Marie
1 2 Bob
1 3 John
1 4 Lola
2 1 Alex
2 2 John
2 3 Kate
2 4 Herald
3 1 Alec
3 2 Paul
3 3 Rex
As you can see data frame is sorted by block_id and then by step. I want to delete only in one block_id everything after the row where I have name John(the row with John as well). So the desired output would be
Block_id step name
1 1 Marie
1 2 Bob
2 1 Alex
3 1 Alec
3 2 Paul
3 3 Rex
Create a CTE that returns for each Block_id the step of the first John.
Then join the table to the CTE:
WITH cte AS (
SELECT Block_id, MIN(step) step
FROM tablename
WHERE name = 'John'
GROUP BY Block_id
)
DELETE FROM tablename t
USING cte c
WHERE c.Block_id = t.Block_id AND c.step <= t.step
See the demo.

Counting current and longest streaks of a given value

I have 2 tables Person (ID, NAME, CLAN_ID) and DailyScore (PERSON_ID, CLAN_ID, DAY_NUMBER, SCORE).
SCORE can take the values "A", "B", "C" or "-" ("-" means absent).
I need to make 2 separate queries to get, for a given CLAN_ID:
the current streak of a given score (let's say A, e.g.) for each Person and the name of the Person, ordered by streak length DESC
the longest ever streak of a given score (let's say A, e.g.) for each Person and the name of the Person, ordered by streak length DESC
An important constraint is that "-" SCORES are ignored, as they represent absences, not real Scores.
Example data:
Table Person:
_ID NAME CLAN_ID
1 John 11
2 Alice 11
3 Bob 12
4 Sara 12
Table DailyScore:
PERSON_ID CLAN_ID DAY_NUMBER SCORE
1 11 1 A
1 11 2 A
1 11 3 A
1 11 4 C
1 11 5 A
2 11 1 B
2 11 2 C
2 11 3 B
2 11 4 A
2 11 5 A
3 12 1 A
3 12 2 A
3 12 3 A
3 12 4 A
3 12 5 B
4 12 1 C
4 12 2 B
4 12 3 C
4 12 4 A
4 12 5 -
Desired result example 1 (CLAN_ID=11, SCORE=A):
Current streak:
Alice 2
John 1
Longest ever streak:
John 3
Alice 2
Desired result example 2 (CLAN_ID=12, SCORE=A):
Current streak:
Sara 1*
Bob 0
*since "-" are ignored, Sara has a current streak of 1 A score
Longest ever streak:
Bob 4
Sara 1
Edit:
In case it helps, here's this example in SQL Fiddle: http://sqlfiddle.com/#!7/2ed69/2
The first query can be:
select
id, name, max(s) as streak
from (
select
p.id,
p.name,
count(*) over(partition by p.id order by s.day_number desc) as c,
sum(case when s.score = 'A' then 1 else 0 end)
over(partition by p.id order by s.day_number desc) as s
from person p
join dailyscore s on s.person_id = p.id
) x
where c = s
group by id, name

Cross Join for Missing table (Select all and Select or Insert the Missing Row Only)

I have two table and have to fill in a list of missing values in one of the table based on the other one. First table has student's information and the second table has Grade related info, Grade and Grade description.
Table One
ID Name yearWithUs Grade Course Level
1 Jim 2004 4 4
2 Jim 2004 4 1
2 Jim 2003 3 3
4 Jim 2002 2 3
4 Jim 2002 2 1
3 Jim 2001 1 2
3 Jim 2001 1 1
Table two -- logic is.. A Student in a higher Course Level can change to a lower Course Level at anytime during the semester. And It can only go downward 1 level at a time. Example: Jim in his second grade first was assigned to attend course in level 3. He need to attend course in level 2 first before he can attend course in level 1. Means. Row for course level 2 at jim's first grade is missing.
Table Two
ID Grade Grade_Desc Course Level Course Desc
1 1 First Grade 1 Basic
2 1 First Grade 2 Normal
3 1 First Grade 3 Hard
4 1 First Grade 4 Expert
5 2 Second Grade 1
6 2 Second Grade 2
7 2 Second Grade 3
8 2 Second Grade 4
. . .
. . .
. . .
Logic of Table Two
ID Grade Grade_Desc Course Level Possible Move
1 1 First Grade 1 Null
2 1 First Grade 2 1
3 1 First Grade 3 2
4 1 First Grade 4 3
Ouptput one ... how to use select statement to return Jim's Grade?
ID Name Grade_Desc Grade yerWithUs Course Level
1 Jim Fourth Grade 4 2004 4
2 Jim Fourth Grade 4 2004 3
3 Jim Fourth Grade 4 2004 2
4 Jim Fourth Grade 4 2004 1
5 Jim Third Grade 3 2003 3
6 Jim Second Grade 2 2002 3
7 Jim Second Grade 2 2002 2
8 Jim Second Grade 2 2002 1
9 Jim First Grade 2 2001 2
10 Jim First Grade 2 2001 1
Output Two..How to retrieve only the missing row into a new temp table?
ID Name Grade_Desc Grade yearWithUs Course Level
2 Jim Fourth Grade 4 2004 3
3 Jim Fourth Grade 4 2004 2
7 Jim Second Grade 2 2002 2
I am currently is using a messy Cursor Statement to do it. The structure looks really messy and hard to debug return errors. I did a lot of research, and saw people use Cross Join to fill the missing portion which looks really clean (See example below)... I have tried the script it myself in many different way by using the cross join example below...obviously, I failed. I found a similar question in stackoverflow..but I am not able to understand how does it work and why without looking at the data....I need help to understand how to use cross join to rerun missing row? and I am open to any other possible solution.
"SELECT calendar.Date,
Category.Cat,
Score = ISNULL(Scores.Score, 0)
FROM Calendar
CROSS JOIN Catogory
LEFT JOIN Scores
ON Scores.Cat = Category.Cat
AND Scores.Date = Calendar.Date
WHERE Calendar.DayOfMonth = 1;"
Inserting missing rows with a join
Thank You
This will produce that output:
select distinct name, grade, Grade_Desc
from one
cross join two
If select is all you want then:
Select row_number() over(order by (select 1)) as id, * from
(Select distinct name from t1)t1
cross join t2
Here is fiddle http://sqlfiddle.com/#!6/a8a42/3
Try this out:
Create #Temp
DECLARE #Name VARCHAR(100) = 'Jim'
SELECT ROW_NUMBER() OVER (ORDER BY B.Grade DESC,B.CourseLevel DESC) ID,
A.Name,
B.Grade_Desc,
B.Grade,
A.YearWithUs,
B.[Course Level]
INTO #temp
FROM
(
SELECT DISTINCT Name,YearWithUs,Grade
FROM TableOne
WHERE Name = #Name
) A
INNER JOIN TableTwo B
ON A.Grade = B.Grade
Output One
SELECT *
FROM #temp
Output Two into #OutputTwo(temp table)
SELECT A.* INTO #OutputTwo
FROM #temp A
LEFT JOIN TableOne B
ON A.Grade = B.Grade
AND A.[Course Level] = B.[Course Level]
WHERE A.Grade IS NULL AND A.[Course Level] IS NULL