Select multiple rows from table and join as CSV - android

Lets say I've got two tables:
album
album_id
album_name
and
song
album_id
song_name
track_number
and I'd like to create a SQL query which, given an album_id, will return the album_name in one field and all the song_names in a comma separated list, ordered by track_number in another field.
The end result would be a cursor with one row, two columns:
album_name : "Some Album Name"
songs: "song1, song2, song3, song4"
I think I need something like this:
SELECT a.*, group_concat(s.name ORDERBY s.track_number SEPERATOR ', ')
FROM album a
LEFT JOIN songs s
WHERE (a.album_id=1)
This is wrong but I think I'm on the right lines
Is this possible in sqlite3 on Android? Can you tell me how?
** EDIT **
Following on from ypercube's excellent answer, which works, is there a way to add another field, much like song but lets call it musician.
So the new table is
musician
album_id
musician_name
musician_number
and my cursor would now give me a result of :
album_name : "Some Album Name"
songs: "song1, song2, song3, song4"
musician: "musician1, musician2, musician3"
with the musician value sorted on the musician_number?

I don't think you can choose order in sqlite's GROUP_CONCAT(), only separator. See the Aggregate functions in SQLite:
SELECT a.*
, group_concat(s.name, ', ')
FROM album a
LEFT JOIN songs s
ON s.album_id = a.album_id
WHERE a.album_id = 1 --- skip this line for all the albums to be displayed
GROUP BY a.album_id
or this to (hopefully) get the order you want:
SELECT a.*
, song_list
FROM a
INNER JOIN
( SELECT album_id
, group_concat(name, ', ') song_list
FROM
( SELECT a.album_id
, s.name
FROM a
LEFT JOIN songs s
ON s.album_id = a.album_id
WHERE a.album_id = 1
ORDER BY a.album_id
, s.track_number
) g
GROUP BY a.album_id
) grp
ON grp.album_id = a.album_id
If you have another table to group_concat:
SELECT a.*
, song_list
, artist_list
FROM a
INNER JOIN
( SELECT album_id
, group_concat(name, ', ') song_list
FROM
( SELECT a.album_id
, s.name
FROM a
LEFT JOIN songs s
ON s.album_id = a.album_id
--- WHERE a.album_id = 1
ORDER BY a.album_id
, s.track_number
) g
GROUP BY a.album_id
) grp
ON grp.album_id = a.album_id
INNER JOIN
( SELECT album_id
, group_concat(artist_name, ', ') artist_list
FROM
( SELECT a.album_id
, r.artist_name
FROM a
LEFT JOIN artists r
ON r.album_id = a.album_id
--- WHERE a.album_id = 1
ORDER BY a.album_id
, r.artist_order
) g2
GROUP BY a.album_id
) grp2
ON grp2.album_id = a.album_id

You can't do so directly but you can fetch the desired fields via a regular query of the like select album_name, song_name from album natural join song ... ... and then iterate the resultset with your cursor to build a comma separated string. When you finish building the CSstring you can write it on a CSV file.

Related

SQLite - Fastest way to count inside selects

I have an application that makes quite a lot of selects, listing its results and allowing the user edit certain values.
The core of my question is if its possible to improve my queries or not given the following query in SQLite:
"SELECT X.data1, X.data2, count(X.id_X) as Quantity_Itens,
(select count(*) from Table2 where id_X=X.id_X) local_Table2,
(select count(*) from Table3 inner join Table2 on Table3.id_Table2 = Table2.id where Table3.id_X=X.id_X and type=1) Quantity_Type1,
(select count(*) from Table3 inner join Table2 on Table3.id_Table2 = Table2.id where Table3.id_X=X.id_X and type=2) Quantity_Type2,
(select count(*) from Table4 where id_X = X.id_X) Quantity_Other,
(select count(*) from Table2 where id_X = X.id_X and status <10) Total_Data FROM Table1 X where (X.type_item = 2 or X.type_item = 4 or X.type_item = 6 or X.type_item = 8) and X.ative = 1 and id_local != 0 group by X.id_X order by X.Alias1"
I am not sure if using promisses will improve in any way this, as I need all those datas before allowing the user to take control again.
Also, may or may not be relevant:
OS: Android 4+
Frameworks: Ionic1, AngularJS, Cordova
In your query there are 5 subqueries executed for each of the rows of Table1.
Only one of them accesses only once 1 table.
2 of them access the same table twice and 2 of them access 2 joined tables twice.
This means there are multiple scans through the same tables for each of the rows of Table1.
Also you are aggregating inside Table1, with GROUP BY id_X, but you have in the SELECT list 2 columns: data1 and data2, which are not included in the GROUP BY clause. This means that the returned values of these columns are arbitrary.
And what is that column Alias1? There is no such column among the returned columns of the query.
Anyway, I suggest that you aggregate first in each table, or join of tables and then join to Table1.
Like this:
SELECT t1.data1,
t1.data2,
t1.Quantity_Items,
t2.local_Table2,
t3.Quantity_Type1,
t3.Quantity_Type2,
t4.Quantity_Other,
t2.Total_Data
FROM (
-- The values returned for data1, data2 and Alias1 will be arbitrary
SELECT id_X, data1, data2, Alias1, COUNT(*) Quantity_Items
FROM table1
GROUP BY id_X
) t1
INNER JOIN (
SELECT id_X, COUNT(*) local_Table2, SUM(status < 10) Total_Data
FROM Table2
GROUP BY id_X
) t2 ON t2.id_X = t1.id_X
INNER JOIN (
SELECT t3.id_X, SUM(type = 1) Quantity_Type1, SUM(type = 2) Quantity_Type2
FROM Table3 t3 INNER JOIN Table2 t2
ON t3.id_Table2 = t2.id
GROUP BY t3.id_X
) t3 ON t3.id_X = t1.id_X
INNER JOIN (
SELECT id_X, COUNT(*) Quantity_Other
FROM Table4
GROUP BY id_X
) t4 ON t4.id_X = t1.id_X
WHERE t1.type_item IN (2, 4, 6, 8)
AND t1.active = 1 AND t1.id_local <> 0
ORDER BY t1.Alias1

Adding two extra columns to result set by joining other tables

I have 4 tables:
tasks table
(task_id , department_id , task_title , task_description , task_start_date , task_due_date , task_rating , task_is_completed)
employees table
(employee_id , department_id , employee_name , employee_salary , employee_hire_date)
departments table
(department_id , department_name)
employees_tasks join table
(employee_id , task_id)
Each table is an entity in room database.
I want to return 2 extra columns with the (select * from employees)
one is for calculating employee's rating (by getting average of task_rating column in tasks table the tasks must be completed) the other column is to show the number of tasks running for that employee (by getting count of rows in tasks with task_is_completed = 0 )
I don't know which table to join with which table. we managed to make two separate SQL statements that return those 2 columns by using union and left joins but they are pretty ugly and when combining them it doesn't work.
what we have tried
select employees.employee_name , employees.employee_id ,avg(tasks.task_rating) as Ratings from employees , tasks inner join employees_tasks on(employees.employee_id = employees_tasks.employee_id )AND tasks.task_id = employees_tasks.task_id where tasks.task_is_completed = 1 group by (employees.employee_name )
union select employees.employee_name, employees.employee_id, avg(0) as Ratings from employees where employees.employee_id not in (select employees.employee_id from employees , tasks inner join employees_tasks on(employees.employee_id = employees_tasks.employee_id ) AND tasks.task_id = employees_tasks.task_id where tasks.task_is_completed = 1 group by (employees.employee_name ) ) group by employees.employee_id order by employees.employee_id ;
select employees.employee_name , employees.employee_id ,count(tasks.task_title) as tasks_Running from employees , tasks inner join employees_tasks on(employees.employee_id = employees_tasks.employee_id )AND tasks.task_id = employees_tasks.task_id where tasks.task_is_completed = 0 group by (employees.employee_name )
union select employees.employee_name , employees.employee_id ,0 as tasks_Running from employees where (employees.employee_id not in (select employees.employee_id from employees , tasks inner join employees_tasks on(employees.employee_id = employees_tasks.employee_id )AND tasks.task_id = employees_tasks.task_id where tasks.task_is_completed = 0 group by (employees.employee_name )))group by (employees.employee_name) order by employees.employee_id ;
We want the output to be like this
(employee_id , department_id , employee_name , employee_salary , employee_hire_date , ratings , numTasksRunning)
I believe the following may suit :-
WITH
-- Common Table Expression 1 - Average of Completed Tasks per employee
employee_completedtask_info AS (
SELECT employees.employee_id,avg(tasks.task_rating) AS atr
FROM employees_tasks
JOIN tasks ON employees_tasks.task_id = tasks.task_id
JOIN employees ON employees_tasks.employee_id = employees.employee_id
WHERE tasks.task_is_completed > 0
GROUP BY employees.employee_id
),
-- Common Table Expression 2 - Incompleted Taks per employee
employee_notcompleted_info AS (
SELECT employees.employee_id,count() AS itc
FROM employees_tasks
JOIN tasks ON employees_tasks.task_id = tasks.task_id
JOIN employees ON employees_tasks.employee_id = employees.employee_id
WHERE tasks.task_is_completed = 0
GROUP BY employees.employee_id
),
-- Common Table Expression 3 - Total Tasks per Employee
employee_total_tasks AS (
SELECT employees.employee_id,count() AS ttc
FROM employees_tasks
JOIN tasks ON employees_tasks.task_id = tasks.task_id
JOIN employees ON employees_tasks.employee_id = employees.employee_id
GROUP BY employees.employee_id
)
SELECT employees.employee_name,
CASE WHEN atr IS NOT NULL THEN atr ELSE 0 END AS average_completed_task_rating,
CASE WHEN itc IS NOT NULL THEN itc ELSE 0 END AS incomplete_task_count,
CASE WHEN ttc IS NOT NULL THEN ttc ELSE 0 END AS total_task_count
FROM employees
LEFT JOIN employee_completedtask_info ON employees.employee_id = employee_completedtask_info.employee_id
LEFT JOIN employee_notcompleted_info ON employees.employee_id = employee_notcompleted_info.employee_id
LEFT JOIN employee_total_tasks ON employees.employee_id = employee_total_tasks.employee_id
;
based upon data generated as per the following :-
DROP TABLE IF EXISTS employees;
CREATE TABLE IF NOT EXISTS employees (employee_id INTEGER PRIMARY KEY, department_id INTEGER, employee_name TEXT, employee_salary REAL, employee_hire_date TEXT);
DROP TABLE IF EXISTS departments;
CREATE TABLE IF NOT EXISTS departments (department_id INTEGER PRIMARY KEY, department_name TEXT);
DROP TABLE IF EXISTS employees_tasks;
CREATE TABLE IF NOT EXISTS employees_tasks (employee_id INTEGER, task_id INTEGER, PRIMARY KEY(employee_id, task_id));
INSERT INTO departments VALUES
(null,'Maths'),(null,'English'),(null,'Craft')
;
INSERT INTO employees VALUES
(null,1,'Fred',55000,'2000-01-02'),
(null,2,'Mary',62000,'1996-03-20'),
(null,3,'Tom',52000,'2004-10-11'),
(null,3,'Susan',72000,'1999-06-14'),
(null,2,'Bert',66000,'2000-10-15'),
(null,1,'Jane',70000,'1992-04-02')
;
INSERT INTO tasks VALUES
(null,3,'Task 001 - Craft','Do the Craft thinggy','2018-01-01','2018-08-19',10,0),
(null,1,'Task 002 - Maths','Do the Maths thinggy','2018-03-14','2019-03-13',20,0),
(null,2,'Task 003 - English','Do the English thinggy','2018-02-14','2018-09-14',8,0),
(null,3,'Task 004 - Craft','Do the Craft job','2018-01-01','2018-08-19',10,1),
(null,1,'Task 005 - Maths','Do the Maths job','2018-03-14','2019-03-13',20,1),
(null,2,'Task 006 - English','Do the English job','2018-02-14','2018-09-14',8,1),
(null,3,'Task 007 - Craft','Craft thinggy','2018-03-03','2018-11-21',10,0),
(null,1,'Task 008 - Maths','Maths thinggy','2018-03-14','2019-03-13',20,0),
(null,2,'Task 009 - English','English thinggy','2018-02-14','2018-09-14',8,0)
;
INSERT INTO employees_tasks VALUES
(1,2),(1,5),(1,8),(1,6),
(2,2),
(3,1),(3,4),(3,7)
;
This results in :-
Note This converts null entries to 0's (i.e. in the above there are no tasks for Susan, Bert and Jane so nulls for their task counts/averages, which complicates matters a little hence the CASE WHEN ... THEN ... ELSE .... END AS clauses).
Note I've included the total tasks counts as this may be useful/wanted (the third CTE extracts this info)

How to get the data from Sqlite database

i'm a new android developer i want get the data from sqlite database from three tables such as ItemMaster,ItemGroupMaster,SalesDetail
ItemMaster
ItemGroupMaster
SalesDetails
After using this query : select sd.salesDate,igm.groupName,im.itemName,im.itemprice from salesdetails sd inner join itemmaster im on sd.itemId=im.itemId inner join itemgroupmaster igm on im.groupId=igm.groupId
Result
Now i want get actual result is look like below image
Please help me
Thanks in advance
Try this. The Qty and NetAmount columns are both aggregates, and NetAmount relies on Qty, so I used a couple of nested queries:
SELECT Y.salesDate
, Y.GroupName
, Y.ItemName
, Y.Qty
, Y.Price
, Y.Price * Y.Qty AS NetAmount
FROM
(SELECT X.salesDate
, X.groupName AS GroupName
, X.itemName AS ItemName
, X.itemprice AS Price
, COUNT(*) AS Qty
FROM
(SELECT sd.salesDate
,igm.groupName
,im.itemName
,im.itemprice
FROM salesdetails sd
INNER JOIN itemmaster im
ON sd.itemId = im.itemId
INNER JOIN itemgroupmaster igm
ON im.groupId = igm.groupId
) X
GROUP BY 1,2,3
) Y
GROUP BY 1,2,3,4,5
ORDER BY 1,2,3,4,5

Row data as column in time keeper

I have data from table CheckInOut
select UserEnrollNumber,TimeDate,TimeStr from DATACHAMCONG.dbo.CheckInOut
UserEnrollNumber TimeDate TimeStr
50559 2015-01-03 00:00:00.000 2015-01-03 07:21:32.000
50559 2015-01-03 00:00:00.000 2015-01-03 16:28:36.000
I want to display as:
UserEnrollNumber TimeDate IN1 OUT1 IN2 OUT2 IN3 OUT3
50559 2014-01-03 2015-01-03 07:21:32.000 2014-01-03 17:11:22.000 NULL NULL NULL NULL
what kind support for me?
SAMPLE TABLE
CREATE TABLE #TEMP(UserEnrollNumber INT,TimeDate DATETIME,TimeStr DATETIME)
INSERT INTO #TEMP
SELECT 50559, '2015-01-03 00:00:00.000', '2015-01-03 07:21:32.000'
UNION ALL
SELECT 50559, '2015-01-03 00:00:00.000', '2015-01-03 16:28:36.000'
UNION ALL
SELECT 50559, '2015-01-04 00:00:00.000', '2015-01-04 07:15:32.000'
UNION ALL
SELECT 50559, '2015-01-04 00:00:00.000', '2015-01-04 08:13:36.000'
UNION ALL
SELECT 50559, '2015-01-04 00:00:00.000', '2015-01-04 09:28:36.000'
UNION ALL
SELECT 50559, '2015-01-04 00:00:00.000', '2015-01-04 16:15:32.000'
UNION ALL
SELECT 70987, '2015-01-03 00:00:00.000', '2015-01-03 04:08:32.000'
UNION ALL
SELECT 70987, '2015-01-03 00:00:00.000', '2015-01-03 14:01:36.000'
UNION ALL
SELECT 70987, '2015-01-05 00:00:00.000', '2015-01-05 06:18:32.000'
UNION ALL
SELECT 70987, '2015-01-05 00:00:00.000', '2015-01-05 15:21:36.000'
QUERY
I have written the logic inside the query.
SELECT UserEnrollNumber,CAST(TimeDate AS DATE)TimeDate,
TimeStr,[STATUS]+CAST(RNO AS VARCHAR(30))[STATUS],
-- Logic to order columns in pivot like IN1,OUT1,IN2,OUT2....
DENSE_RANK() OVER (ORDER BY RNO,[STATUS]) ORG
INTO #NEWTABLE
FROM
(
SELECT *,
-- 1st record will be IN next OUT next IN next OUT.....
CASE WHEN ROW_NUMBER() OVER(PARTITION BY UserEnrollNumber ORDER BY UserEnrollNumber,TimeDate,TimeStr)%2 = 0
THEN 'OUT' ELSE 'IN' END [STATUS],
-- Check the count of In-out in a date
(ROW_NUMBER() OVER(PARTITION BY UserEnrollNumber,CAST(TimeDate AS DATE) ORDER BY UserEnrollNumber,CAST(TimeDate AS DATE),TimeStr)+1)/2 RNO
FROM #TEMP
)TAB
ORDER BY UserEnrollNumber,CAST(TimeDate AS DATE),TimeStr
Get the columns for dynamic pivot
DECLARE #cols NVARCHAR (MAX)
SELECT #cols = COALESCE (#cols + ',[' + [STATUS] + ']','[' + [STATUS] + ']')
FROM (SELECT DISTINCT ORG,[STATUS] FROM #NEWTABLE) PV
ORDER BY ORG
Now pivot the query
DECLARE #query NVARCHAR(MAX)
SET #query = 'SELECT * FROM
(
SELECT UserEnrollNumber,TimeDate, TimeStr,[STATUS]
FROM #NEWTABLE
) x
PIVOT
(
MIN(TimeStr)
FOR [STATUS] IN (' + #cols + ')
) p
ORDER BY UserEnrollNumber,TimeDate'
EXEC SP_EXECUTESQL #query
Click here to see the working result
I'm excute this query and recieve this error :
(16561 row(s) affected)
Msg 325, Level 15, State 1, Line 6
Incorrect syntax near 'PIVOT'. You may need to set the compatibility level of the current database to a higher value to enable this feature. See help for the SET COMPATIBILITY_LEVEL option of ALTER DATABASE.

SUM query in multiple table with group by sqlite database

Hello friends i have three tables property_master , rent_master,expense_master and fields are as below
property_master --> p_id, p_name
rent_master --> r_id,p_id,r_amount
expense_master --> e_id,p_id,e_amount
i want to total sum of r_amount, and e_amount with single query so my query is as below
SELECT p.p_id AS "Product Code",
p.p_name AS "Description",
SUM(CASE WHEN ri.r_amount IS NULL THEN 0 ELSE ri.r_amount END) AS "Quantity" ,
SUM(CASE WHEN d.e_amount IS NULL THEN 0 ELSE d.e_amount END) AS "DQuantity"
FROM property_master AS p
LEFT JOIN rent_master AS ri ON ri.p_id = p.p_id
LEFT JOIN expense_master AS d ON d.p_id = p.p_id
GROUP BY p.p_id
ORDER BY SUM(ri.r_amount) DESC,
SUM(d.e_amount) DESC
When i run above code it will give right value for r_amount but for e_amount it will give double value for that so any idea how can i solve this?
When there are two different rent_master rows with the same p_id values, you get two joined rows for each matching expense_master row.
You have to compute the sums with independent subqueries:
SELECT p_id AS "Product Code",
p_name AS "Description",
(SELECT SUM(r_amount)
FROM rent_master
WHERE p_id = property_master.p_id
) AS "Quantity",
(SELECT SUM(e_amount)
FROM expense_master
WHERE p_id = property_master.p_id
) AS "DQuantity"
FROM property_master
ORDER BY Quantity DESC,
DQuantity DESC

Categories

Resources