Loop through table, use conditionals, then store value in another table - sql

The procedure is to fill the "City" column in Table B based on the "Letter" column from Table A.
TABLE A
+----------+-------+
| Number | Letter|
+----------+-------+
| 1 | A |
| 1 | |
| 1 | |
| 2 | |
| 2 | |
| 3 | |
| 3 | B |
| 3 | |
| 3 | C |
+----------+-------+
TABLE B
+----------+-------+
| AC | City |
+----------+-------+
| 1 | A |
| 1 | A |
| 1 | A |
| 1 | A |
| 2 | |
| 2 | |
| 2 | |
| 2 | |
| 3 | B |
| 3 | B |
| 3 | B |
+----------+-------+
If AC=1, refer to Number=1, and loop through the "Letter" values from top to bottom to get the top-most value.
For Number=1, the topmost value is A, so for AC=1, fill in all "City" column as A.
For AC=2, Number=2, and there are no values in Table A, so fill in all "City" for each AC=2 as blank.
For AC=3, Number=3, and the top-most value is B, so fill in all "City" for each AC=3 as B.
How do you code this in standard SQL?
I am using the Caspio software and will be inserting the SQL into the "City" column itself, but that shouldn't interfere too much with the code.
This is what I have so far:
SELECT Letter
FROM TableA
WHERE TableA.Number = TableB.AC
AND TableA.Number != ""
LIMIT 1
But it doesn't seem to be working, and I think it's necessary to loop through Table A to find the City value for each AC=Number.
Thanks for any help.
EDIT:
I have figured out the solution:
SELECT TOP 1 Letter
FROM TableA
WHERE Letter !='' AND Number=AC
Thanks.

It doesnt work because you are not including tableB in your FROM clause, or joining it. You can try this one:
SELECT Letter FROM TableA WHERE Number IN
(SELECT AC FROM TableB WHERE City!='' AND City IS NOT NULL)
AND Letter!='' AND LETTER IS NOT NULL

First things first, don't think of "looping" in SQL, it means that you're thinking about the problem wrong. You can to use set-based thinking.
So think about what you want to do, not how you want to do it.
You want to update the TableB.City based on the value of TableA.Letter
UPDATE TableB
SET City = Letter
FROM
(
SELECT Number, Letter,ROW_NUMBER () OVER ( PARTITION BY Number order by number ) AS SortOrder
FROM TableA
WHERE Letter IS NOT NULL AND Letter != ''
) AS A
WHERE A.SortOrder = 1 AND TableB.AC = A.number
I have included the Row_Number sorting, this is to ensure you get the first letter. Please note that you should order by your PK, assuming you have one and assuming that it's an IDENTITY and an int
See the sqlFiddle
EDIT
Sure, you can just do a select.
SELECT TableB.AC, A.Letter
FROM
(
SELECT Number, Letter,ROW_NUMBER () OVER ( PARTITION BY Number order by number ) AS SortOrder
FROM TableA
WHERE Letter IS NOT NULL AND Letter != ''
) AS A
LEFT OUTER JOIN TableB.AC = A.number
WHERE A.SortOrder = 1

Related

Returning rows with the same ID but exclude some on second column

I've seen similar questions about but not quite hitting the nail on the head for what I need. Lets say I have a table.
+-----+-------+
| ID | Value |
+-----+-------+
| 123 | 1 |
| 123 | 2 |
| 123 | 3 |
| 456 | 1 |
| 456 | 2 |
| 456 | 4 |
| 789 | 1 |
| 789 | 2 |
+-----+-------+
I want to return DISTINCT IDs but exclude those that have a certain value. For example lets say I don't want any IDs that have a 3 as a value. My results should look like.
+-----+
| ID |
+-----+
| 456 |
| 789 |
+-----+
I hope this makes sense. If more information is needed please ask and if this has been answered before please point me in the right direction. Thanks.
You can use group by and having:
select id
from t
group by id
having sum(case when value = 3 then 1 else 0 end) = 0;
The having clause counts the number of "3"s for each id. The = 0 returns only returns groups where the count is 0 (i.e. there are no "3"s).
You can use not exists :
select distinct t.id
from table t
where not exists (select 1 from table t1 where t1.id = t.id and t1.value = 3);
Try this:
select id from tablename
group by id
having (case when value=3 then 1 else 0 end)=0
You can also use EXCEPT for comparing following two data sets that will give the desired result set
select distinct Id from ValuesTbl
except
select Id from ValuesTbl where Value = 3

Remove dulicate rows using SQL

I want to know if there is a way to remove duplicate values from a table. The key 'distinct' will fetch us the unique rows however if one value differs in a column, it wont. so just wanted to know if this can be achieved by any means. Hope the below example will help.
For example : In the below table there are two entries for Emp_ID 1234 with two different priorities. my output should consider the higher priority row alone. Is it possible?
My table
+---------+------+--------+-------+
| Employee_ID| priority | gender |
+------------+-----------+--------+
| 1234 | 1 | F |
| 1234 | 10 | F |
| 5678 | 2 | M |
| 5678 | 25 | M |
| 9101 | 45 | F |
+------------+-----------+--------+
Output
+---------+------+--------+-------+
| Employee_ID| priority | gender |
+------------+-----------+--------+
| 1234 | 1 | F |
| 5678 | 2 | M |
| 9101 | 45 | F |
+------------+-----------+--------+
DELETE
FROM Table t
WHERE EXISTS ( SELECT Employee_ID FROM Table WHERE Employee_ID = t.Employee_ID AND priority < t.Priority)
That is if you really want to remove them from the table. The Exists part can also be used in a select query to leave the values in the Original table.
SELECT *
FROM Table t
WHERE NOT EXISTS (SELECT Employee_ID FROM Table WHERE Employee_ID = t.Employee_ID AND priority > t.Priority)
select Employee_ID,max(priority) as priority,gender
from table
group by Employee_ID,gender

SQL DELETE group of records based on opposite group being empty

In table T, I'm trying to delete all records in a groups having same value of A, but only if all members of this group have B set to 'x'.
Given the Table T:
+-------+--------+
| A | B |
+-------+--------+
| 2 | '' |
| 2 | 'x' |
| 2 | '' |
| 8 | 'x' |
| 8 | 'x' |
| 15 | '' |
| 15 | '' |
+-------+--------+
The two records with A == 8 have to be deleted as all two of them have B==1. The group of A==2 has mixed value of B so it stays. And group of A==15 doesn't have all of it's B equal to 1 it also stays.
Is this possible to do by one query?
If not, any other way that is fast enough for a table with a lot of records?
you can try this query:
delete from T
where A in (
select A
from T
group by A
having sum(B) = count(*)
)
if column b can contain non 0/1 values, you can add additional conditions:
having sum(B) = count(*) and min(b)=1 and max(b)=1
if you can't use numeric values, you can just use min/max, like
having min(b)='x' and max(b)='x'
Try this. Group by and Having with some aggregate should work
DELETE FROM tablename
WHERE a IN(SELECT a
FROM tablename
GROUP BY a
HAVING count(case when b='x' then 1 end) = Count(b)

Exclude a group when one row match in another table

I have two tables :
the first one called "card" with one column "id".
| id |
| 1 |
| 2 |
| 3 |
| .. |
The second table is named "waste" with two columns "card_id" and "waste_type".
| card_id | waste_type |
| 1 | 1 |
| 1 | 3 |
| 2 | 2 |
| 2 | 1 |
And i want to select only the card where there is no waste_type = 2
The query should look like this :
SELECT c.id FROM card c
JOIN waste w
ON c.id = w.card_id
WHERE waste_type <> 2
I want this result :
id
1
But i get :
id
1
2
How can i do that ? Thank you so much in advance !
You should use a not exists clause for that.
select c.id
from card c
where not exists (select null from waste w
where w.card_id = c.id
and w.waste_type = 2)
With your query, I would guess you rather retrieve
1
1
2

Selecting column from one table and count from another

t1
id | name | include
-------------------
1 | foo | true
2 | bar | true
3 | bum | false
t2
id | some | table_1_id
-------------------------
1 | 42 | 1
2 | 43 | 1
3 | 42 | 2
4 | 44 | 1
5 | 44 | 3
Desired output:
name | count(some)
------------------
foo | 3
bar | 1
What I have currently from looking through other solutions here:
SELECT a.name,
COUNT(r.some)
FROM t1 a
JOIN t2 r on a.id=r.table_1_id
WHERE a.include = 'true'
GROUP BY a.id,
r.some;
but that seems to get me
name | count(r.some)
--------------------
foo | 1
foo | 1
bar | 1
foo | 1
I'm no sql expert (I can do simple queries) so I'm googling around as well but finding most of the solutions I find give me this result. I'm probably missing something really easy.
Just remove the second column from the group by clause
SELECT a.name,
COUNT(r.some)
FROM t1 a
JOIN t2 r on a.id=r.table_1_id
WHERE a.include = 'true'
GROUP BY a.name
Columns you want to use in an aggregate function like sum() or count() must be left out of the group by clause. Only put the columns in there you want to be unique outputted.
This is because multiple column group requires the all column values to be same.
See this link for more info., Using group by on multiple columns
Actually in you case., if some are equal, table_1_id is not equal (And Vice versa). so grouping cannot occur. So all are displayed individually.
If the entries are like,
id | some | table_1_id
-------------------------
1 | 42 | 1
2 | 43 | 1
3 | 42 | 2
4 | 42 | 1
Then the output would have been.,
name | count
------------------
foo | 2 (for 42)
foo | 1 (for 43)
bar | 1 (for 42)
Actually, if you want to group on 1 column as Juergen said, you could remove r.some; from groupby clause.