select distinct sum of item that can exists in several tables - sqlite

Let's say that i have 3 tables: Articles1,Articles2,Articles3.
It's possible that same articlegroup exists in two of theese tables.
I only want to sum amount by each articlegroup existing in Articles1 and does not exists in the other tables.
Tables:
Articles1
| Id | ArticleName | Amount |
-----------------------------------------
'1' 'Apple' '2'
'2' 'Orange' '2'
'3' 'Banana' '3'
Articles2
| Id | ArticleName | Amount |
-----------------------------------------
'1' 'Apple' '2'
'2' 'Orange' '2'
Articles3
| Id | ArticleName | Amount |
-----------------------------------------
'1' 'Apple' '2'
'2' 'Orange' '2'
My code:
SELECT SUM(a1.Amount)
FROM Articles1 a1
LEFT OUTER JOIN Articles2 a2
ON a1.Id = a2.Id
LEFT OUTER JOIN Articles3 a3
ON a1.Id = a3.Id
WHERE a1.Id <> a2.Id OR a1.Id <> a3.Id
GROUP BY a1.ArticleName
Fiddle

Modified your query to
select sum(a1.Amount)
FROM Articles1 a1
WHERE
a1.Id not in (select Id from Articles2) and
a1.Id not in (select Id from Articles3)
GROUP BY a1.ArticleName
This returns 3 as output i.e. only for Banana.
Fiddle

Related

Getting Parent Child hierarchy

I'm trying to get ancestors of a child (dog) upto Level 5. For Example in attached picture I'll be sending "Spencer di Casa Massarelli" and in result want to have associated parents (both father and mother). In my DB structure I've used father_id and mother_id.
DB & version: 10.4.11-MariaDB
Table Script:
CREATE TABLE `dogs` (
`dog_id` int(11) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`father_id` int(11) DEFAULT NULL,
`moter_id` int(11) DEFAULT NULL,
PRIMARY KEY (`dog_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `dogs` VALUES ('0', null, null, null);
INSERT INTO `dogs` VALUES ('1', 'Father', null, null);
INSERT INTO `dogs` VALUES ('2', 'Mother', null, null);
INSERT INTO `dogs` VALUES ('3', 'Father1', null, null);
INSERT INTO `dogs` VALUES ('4', 'Mother2', null, null);
INSERT INTO `dogs` VALUES ('5', 'Son', '1', '2');
INSERT INTO `dogs` VALUES ('6', 'Daughter', '3', '4');
INSERT INTO `dogs` VALUES ('7', 'GrandSon', '5', '6');
I've tried following self join query but the problem is I'm unable to get right parents i.e., parents(both father and mother) of first parent.
SELECT t1.name AS lev1,
t2.name AS lev2Father,
t3.name AS lev2Mother,
t4.name AS level3Father,
t5.name AS level3Mother,
t6.name AS level4Father,
t7.name AS level4Mother,
t8.name AS level5Father,
t9.name AS level5Mother,
t10.name AS level6Father,
t11.name AS level6Mother
FROM dogs AS t1
LEFT JOIN dogs AS t2 ON t2.dog_id = t1.father_id
LEFT JOIN dogs AS t3 ON t3.dog_id = t1.mother_id
LEFT JOIN dogs AS t4 ON t4.dog_id = t2.father_id
LEFT JOIN dogs AS t5 ON t5.dog_id = t2.mother_id
LEFT JOIN dogs AS t6 ON t6.dog_id = t4.father_id
LEFT JOIN dogs AS t7 ON t7.dog_id = t4.mother_id
LEFT JOIN dogs AS t8 ON t8.dog_id = t6.father_id
LEFT JOIN dogs AS t9 ON t9.dog_id = t6.mother_id
LEFT JOIN dogs AS t10 ON t10.dog_id = t8.father_id
LEFT JOIN dogs AS t11 ON t11.dog_id = t8.mother_id
WHERE t1.dog_id = 7
WITH RECURSIVE
cte AS (
SELECT *, 0 level, ' ' relation
FROM dogs
WHERE dog_id = 7
UNION ALL
SELECT dogs.*, level + 1, 'father'
FROM dogs
JOIN cte ON cte.father_id = dogs.dog_id
WHERE level < 5
UNION ALL
SELECT dogs.*, level + 1, 'mother'
FROM dogs
JOIN cte ON cte.mother_id = dogs.dog_id
WHERE level < 5
)
SELECT *
FROM cte
ORDER BY level, relation;
fiddle
Result
dog_id | name | father_id | mother_id | level | relation
-----: | :------- | --------: | --------: | ----: | :-------
7 | GrandSon | 5 | 6 | 0 |
5 | Son | 1 | 2 | 1 | father
6 | Daughter | 3 | 4 | 1 | mother
1 | Father | null | null | 2 | father
3 | Father1 | null | null | 2 | father
2 | Mother | null | null | 2 | mother
4 | Mother2 | null | null | 2 | mother

sqlite3 recursive aggregation of data

This may be a kind of the Knapsack problem.
I need to traverse a data table, group it by a column, choosing ones with better time.
Then repeat the previous step until a limit given by column CAPACITY is not reached.
This is the demo scenario:
create table if not exists data( vid num, size num, epid num, sid num, capacity num, dt );
delete from data;
insert into data(vid,size,epid,sid,capacity,dt)
values
(0,20,1,1,50,1100), -- 2nd choice
(0,20,1,1,50,1000), -- 1st choice
(0,20,1,1,50,1200), -- last choice excluded because out of capacity
(1,20,2,2,50,1100), -- 2nd choice
(1,20,2,2,50,1000), -- 1st choice
(1,20,2,2,50,1200); -- last choice excluded because out of capacity
This is the non recursive solution:
with best0 as (
select a.rowid as tid,a.vid,a.sid,a.size,a.dt,a.capacity-a.size as remains,0 as level
from data a
group by a.sid
having min(a.dt)
),
best1 as (
select a.tid,a.vid,a.sid,a.size,a.dt,a.remains, a.level
from (
select
a.rowid as tid,a.sid,a.vid,a.size,a.capacity,a.dt,b.remains-a.size as remains,
b.level+1 as level
from data a
join best0 b on b.sid=a.sid -- and b.level=a.level-1
where not a.rowid in (select tid from best0)
and b.remains-a.size>0
) a group by a.sid having min(a.dt)
),
best2 as (
select a.tid,a.vid,a.sid,a.size,a.dt,a.remains, a.level
from (
select
a.rowid as tid,a.sid,a.vid,a.size,a.capacity,a.dt,b.remains-a.size as remains,
b.level+1 as level
from data a
join best1 b on b.sid=a.sid -- and b.level=a.level-1
where not a.rowid in (select tid from best0 union all select tid from best1)
and b.remains-a.size>0
) a group by a.sid having min(a.dt)
)
select * from best0
union all
select * from best1
union all
select * from best2
And this the result:
tid | vid | sid | size | Dtime | capacity | group_level
--- | --- | --- | ---- | ----- | -------- | -----------
2 | 0 | 1 | 20 | 1000 | 30 | 0
5 | 1 | 2 | 20 | 1000 | 30 | 0
1 | 0 | 1 | 20 | 1100 | 10 | 1
4 | 1 | 2 | 20 | 1100 | 10 | 1
This is the recursive version that give error: "recursive reference in a subquery: best"
with recursive best(tid,vid,sid,size,dt,remains,level)
as (
select a.rowid as tid,a.vid,a.sid,a.size,a.dt,a.capacity-a.size as remains,0 as level
from data a
group by a.sid
having min(a.dt)
union all
select a.tid,a.vid,a.sid,a.size,a.dt,a.remains, a.level
from (
select
a.rowid as tid,a.sid,a.vid,a.size,a.dt,b.remains-a.size as remains,
b.level+1 as level
from data a
join best b on b.sid=a.sid -- and b.level=a.level-1
where not a.rowid in (select tid from best) and b.remains-a.size>0
) a group by a.sid having min(a.dt)
)
select * from best
I tried differents solutions even using a loop counter but everyone give the same error.

Is there a way to reuse subqueries in the same query?

See Update at end of question for solution thanks to marked answer!
I'd like to treat a subquery as if it were an actual table that can be reused in the same query. Here's the setup SQL:
create table mydb.mytable
(
id integer not null,
fieldvalue varchar(100),
ts timestamp(6) not null
)
unique primary index (id, ts)
insert into mydb.mytable(0,'hello',current_timestamp - interval '1' minute);
insert into mydb.mytable(0,'hello',current_timestamp - interval '2' minute);
insert into mydb.mytable(0,'hello there',current_timestamp - interval '3' minute);
insert into mydb.mytable(0,'hello there, sir',current_timestamp - interval '4' minute);
insert into mydb.mytable(0,'hello there, sir',current_timestamp - interval '5' minute);
insert into mydb.mytable(0,'hello there, sir. how are you?',current_timestamp - interval '6' minute);
insert into mydb.mytable(1,'what up',current_timestamp - interval '1' minute);
insert into mydb.mytable(1,'what up',current_timestamp - interval '2' minute);
insert into mydb.mytable(1,'what up, mr man?',current_timestamp - interval '3' minute);
insert into mydb.mytable(1,'what up, duder?',current_timestamp - interval '4' minute);
insert into mydb.mytable(1,'what up, duder?',current_timestamp - interval '5' minute);
insert into mydb.mytable(1,'what up, duder?',current_timestamp - interval '6' minute);
What I want to do is return only rows where FieldValue differs from the previous row. This SQL does just that:
locking row for access
select id, fieldvalue, ts from
(
--locking row for access
select
id, fieldvalue,
min(fieldvalue) over
(
partition by id
order by ts, fieldvalue rows
between 1 preceding and 1 preceding
) fieldvalue2,
ts
from mydb.mytable
) x
where
hashrow(fieldvalue) <> hashrow(fieldvalue2)
order by id, ts desc
It returns:
+----+---------------------------------+----------------------------+
| id | fieldvalue | ts |
+----+---------------------------------+----------------------------+
| 0 | hello | 2015-05-06 10:13:34.160000 |
| 0 | hello there | 2015-05-06 10:12:34.350000 |
| 0 | hello there, sir | 2015-05-06 10:10:34.750000 |
| 0 | hello there, sir. how are you? | 2015-05-06 10:09:34.970000 |
| 1 | what up | 2015-05-06 10:13:35.470000 |
| 1 | what up, mr man? | 2015-05-06 10:12:35.690000 |
| 1 | what up, duder? | 2015-05-06 10:09:36.240000 |
+----+---------------------------------+----------------------------+
The next step is to return only the last row per ID. If I were to use this SQL to write the previous SELECT to a table...
create table mydb.reusetest as (above sql) with data;
...I could then do this do get the last row per ID:
locking row for access
select t1.* from mydb.reusetest t1,
(
select id, max(ts) ts from mydb.reusetest
group by id
) t2
where
t2.id = t1.id and
t2.ts = t1.ts
order by t1.id
It would return this:
+----+------------+----------------------------+
| id | fieldvalue | ts |
+----+------------+----------------------------+
| 0 | hello | 2015-05-06 10:13:34.160000 |
| 1 | what up | 2015-05-06 10:13:35.470000 |
+----+------------+----------------------------+
If I could reuse the subquery in my initial SELECT, I could achieve the same results. I could copy/paste the entire query SQL into another subquery to create a derived table, but this would just mean I'd need to change the SQL in two places if I ever needed to modify it.
Update
Thanks to Kristján, I was able to implement the WITH clause into my SQL like this for perfect results:
locking row for access
with items (id, fieldvalue, ts) as
(
select id, fieldvalue, ts from
(
select
id, fieldvalue,
min(fieldvalue) over
(
partition by id
order by ts, fieldvalue
rows between 1 preceding and 1 preceding
) fieldvalue2,
ts
from mydb.mytable
) x
where
hashrow(fieldvalue) <> hashrow(fieldvalue2)
)
select t1.* from items t1,
(
select id, max(ts) ts from items
group by id
) t2
where
t2.id = t1.id and
t2.ts = t1.ts
order by t1.id
Does WITH help? That lets you define a result set you can use multiple times in the SELECT.
From their example:
WITH orderable_items (product_id, quantity) AS
( SELECT stocked.product_id, stocked.quantity
FROM stocked, product
WHERE stocked.product_id = product.product_id
AND product.on_hand > 5
)
SELECT product_id, quantity
FROM orderable_items
WHERE quantity < 10;

SQL SERVER merge two column, combine it, and count the same

I want to create a barcode by merge and combine two column.
here is my table :
ID | Items1 | Items2 | BArcode
001 | Shirt | CPU |
002 | Shirt | CPU |
001 | Shoes | Monitor |
002 | Jacket | Monitor |
001 | Shoes | CPU |
002 | | Keyboard |
002 | | Keyboard |
001 | Shirt | Keyboard |
002 | Shirt | |
The barcode created based on ID+Item1/Items2+Count. The count get from counting how many times one ID have oredered the same item. I want it to display only the data that haven't generated a barcode, so it's when the Barcode column value is null, from the above table the Barcode is null:
ID | Barcode |
001 | 001Shirt1 |
002 | 002Shirt1 |
001 | 001Shoes1 |
002 | 002Jacket1 |
001 | 001Shoes2 |
001 | 001Shirt2 |
002 | 002Shirt2 |
001 | 001CPU1 |
002 | 002CPU1 |
001 | 001Monitor1 |
002 | 002Monitor1 |
001 | 001CPU2 |
002 | 002Keyboard1 |
002 | 002Keyboard2 |
001 | 001Keyboard1 |
here is my first code :
Dim strcommand As String = "select [ID], ([ID] + [Items1])
as Barcode from tbl_Request where [Items1] != 'null'
and Barcode = 'null' union select ([ID] + [Items2])
from tbl_Request where [Items2] != 'Null' and Barcode = 'null'"
it doesn't work. it say "All queries combined using a UNION, INTERSECT or EXCEPT operator must have an equal number of expressions in their target lists". on my code above I haven't implement the counting yet. does anyone know how to do it?
Thanks in advances....
You can use this query to generate the barcode values:
;with cte as
(select id, item1 item, row_number() over (partition by id, item1 order by getdate()) rn
from items
where item1 is not null and barcode is null
union
select id, item2 item, row_number() over (partition by id, item2 order by getdate()) rn
from items
where item2 is not null and barcode is null)
select id, cast(id as varchar) + item + cast(rn as varchar) barcode
from cte
If you wanted to add this to a new table, say tbl_barcode with columns id and barcode, you would do this:
;with cte as
(select id, item1 item, row_number() over (partition by id, item1 order by getdate()) rn
from items
where item1 is not null and barcode is null
union
select id, item2 item, row_number() over (partition by id, item2 order by getdate()) rn
from items
where item2 is not null and barcode is null)
insert into tbl_barcode (id, barcode)
select id, cast(id as varchar) + item + cast(rn as varchar) barcode
from cte
The error that you are getting is that the union join that you have created does not contain the same fields in the second select statement, as your original select statement. SQL UNION Operator, Notice that each SELECT statement within the UNION must have the same number of columns.
So therefore you will need to change
select ([ID] + [Items2])
to
select [ID], ([ID] + [Items2])
declare #x table (ID varchar(20),Items1 VARCHAR(10),Items2 VARCHAR(10),BARCODE INT)
INSERT INTO #x
(ID,Items1,Items2,BARCODE)
VALUES ('001','Shirt','CPU',NULL)
INSERT INTO #x
(ID,Items1,Items2,BARCODE)
VALUES ('001',NULL,'Monitor',NULL)
INSERT INTO #x
(ID,Items1,Items2,BARCODE)
VALUES ('002','TRouser','Monitor',NULL)
select ID,
Case when Items1 IS NOT NULL Then ID +Items1+
CAST(DENSE_RANK()OVER(PARTITION by Items1 order by Items1 desc)AS VARCHAR)
when Items2 IS NOT NULL Then ID +Items2+
CAST(DENSE_RANK()OVER(PARTITION by Items2 order by Items1 desc)AS VARCHAR)
ELSE '' END AS Barcode from #x

Suggestion needed writing a complex query - sqlite

I have 4 columns in a table called musics - 'artist','genre', 'writer' , 'producer'.
I need to write a query such that, it returns a value 0 , if there are no repetition of values corresponding to the column name; if there is a repetition of values, it should return a value 1, corresponding to that column name.
Any help is much appreciated
SELECT (COUNT(artist) <> COUNT(DISTINCT artist)) artist,
(COUNT(genre) <> COUNT(DISTINCT genre)) genre,
(COUNT(writer) <> COUNT(DISTINCT writer)) writer,
(COUNT(producer) <> COUNT(DISTINCT producer)) producer
FROM musics
Another version
SELECT
( SELECT (COUNT(*) > 0)
FROM (SELECT 1 FROM musics GROUP BY artist HAVING COUNT(*) > 1) a
) artist,
( SELECT (COUNT(*) > 0)
FROM (SELECT 1 FROM musics GROUP BY genre HAVING COUNT(*) > 1) g
) genre,
( SELECT (COUNT(*) > 0)
FROM (SELECT 1 FROM musics GROUP BY writer HAVING COUNT(*) > 1) w
) writer,
( SELECT (COUNT(*) > 0)
FROM (SELECT 1 FROM musics GROUP BY producer HAVING COUNT(*) > 1) p
) producer
Sample data
| artist | genre | writer | producer |
------------------------------------------
| artist1 | genre1 | writer1 | producer1 |
| artist2 | genre2 | writer1 | producer2 |
| artist1 | genre3 | writer3 | producer3 |
Sample output:
| artist | genre | writer | producer |
--------------------------------------
| 1 | 0 | 1 | 0 |
SQLFiddle
For Artist
select convert(bit,(count(1)-1))
from table_name
group by artist -- <-- Replace artist with column name for which duplicate
write a select count statement using distinct with specified column and another select count without distinct and compare both of them based on your requirement
you can use 4 different query with union & each query must contain count(column name) + group by clause

Resources